Plasma GitLab Archive
Projects Blog Knowledge

Module Cache_client

module Cache_client: sig .. end
Client to access cache servers


Theory of operation



Buckets. The client can be used to access one or several cache servers. The whole cache store is divided up in so-called buckets. The user is free to

  • use only a single bucket on a single server
  • put every bucket on a different server
  • even have multiple buckets per server
This scheme is primarily intended for distributing the whole store over multiple servers. Having multiple buckets per server gives some freedom for reorganizing the distribution scheme (e.g. add servers later). The library determines which key lives on which server by computing the e_bucket component of the entries. Functions for doing this can be found in Cache_util:

 
    let e_key = Cache_util.hash_of_key k in
    let e_bucket = Cache_util.bucket_of_hash n e_key
 

The number n is the number of buckets.

Expiration. Entries can have a timestamp that says when the entries are removed from the cache. It is ensured that the get operation never returns an expired entry when both client and server have the same clock. Independent of expiration by time, the cache may also expire entries because the cache becomes too large. The cache implements a LRU semantics: The least recently used entries are removed first. As "use" any get or set operation qualifies.

Deletion. There is a third mechanism for removing entries: One can also actively delete entries. The delete operation takes a delete timestamp as argument. If this timestamp is in the past (e.g. 0), the deletion is performed immediately. Otherwise the entry is scheduled for deletion at the given time. The e_delete_flag says whether such a deletion will happen.

In order to coordinate parallel accesses to the cache, all entries that are scheduled for deletion are locked for overwriting. Any set operation is refused by default. You can break this lock by setting the opt_undelete option. In this case, set not only overwrites but also cancels the scheduled deletion.

The delete operation has effectively a higher precedence than the set operation unless you specify opt_undelete. This makes delete different from setting a new expiration timestamp in the entry by calling set. The higher precedence is useful if you have new knowledge about the validity period of entries, and you want to prevent that restricting the validity period interfers with the regular use of set.

Note that a scheduled deletion does not prevent the entry from expiring because of the expiration timestamp or because of exceeding the maximum size of the cache.

Atomicity. The operations set and delete are either fully conducted, or not conducted at all. For example, if you call set with opt_setifchanged, there are only two outcomes:

  • The set operation is done: The value is overwritten, the new modification timestamp is set, the expiration timestamp is set to the passed value, and any delete flag is removed.
  • The set operation is not done: The entry is not modified at all.
As opt_setifchanged causes that set is only performed for value changes, you get implicitly the effect of only setting the modification and expiration timestamps when the value is distinct from the previous one.

Interface


type key = [ `Hash of Digest.t | `String of string ] 
Keys of hash entries can be given in two ways:
  • `Hash dg: As digest of a string
  • `String s: As string (to be digested)

type timestamp = int64 
The time in seconds since the epoch
type entry = Cache_aux.entry = {
   mutable e_key : Digest.t; (*The key in hashed form*)
   mutable e_creation : timestamp; (*Time of first addition to the cache*)
   mutable e_modification : timestamp; (*Time of last modification in the cache*)
   mutable e_expiration : timestamp; (*Time when the entry will expire; 0 = never*)
   mutable e_delete_flag : bool; (*Whether this entry is scheduled for deletion*)
   mutable e_value : string; (*The value of the entry, an arbitrary string*)
   mutable e_value_hash : Digest.t; (*The MD5 sum of the value*)
   mutable e_value_length : int; (*The length of the value*)
   mutable e_counter : int; (*The number of get accesses to this entry*)
   mutable e_bucket : int; (*The bucket number of this entry*)
}
Entries are the primary contents of the cache
type set_options = Cache_aux.set_options = {
   mutable opt_overwrite : bool; (*Allow overwriting existing entries*)
   mutable opt_add : bool; (*Allow adding new entries*)
   mutable opt_undelete : bool; (*Allow the modification of entries that are scheduled for deletion. Such entries are revived, and count no longer as deleted*)
   mutable opt_setifchanged : bool; (*Modifies opt_overwrite: Overwriting is only permitted if the new value is distinct from the old one*)
}
Modifies the set operation
type get_options = Cache_aux.get_options = {
   mutable opt_novalue : bool; (*Do not return the e_value component of entries, but an empty string as replacement (for getting meta data only)*)
   mutable opt_getifmodifiedsince : timestamp option; (*When Some t: Only return entries that have e_modification >= t. When None: No restriction.*)
   mutable opt_getifnotmd5 : Digest.t option; (*When Some dg: Only return entries when e_value_hash <> dg. When None: No restriction.*)
}
Modifies the get operation
type delete_options = Cache_aux.delete_options = {
   mutable opt_strictlock : bool; (*Forces that the entry is even scheduled for deletion if it does not exist. Unless opt_undelete is given, a set operation cannot be successful for the key while the entry is in the delete queue, i.e. the key is locked until the deletion is really conducted.*)
   mutable opt_delifolderthan : timestamp option; (*When Some t: It is required that e_modification < t in order to schedule an entry for deletion. When None: no such requirement.*)
   mutable opt_delifmd5 : Digest.t option; (*When Some dg: It is required that e_value_hash = dg in order to schedule an entry for deletion. When None: no such requirement.*)
   mutable opt_delifnotmd5 : Digest.t option; (*When Some dg: It is required that e_value_hash <> dg in order to schedule an entry for deletion. When None: no such requirement.*)
}
Modifies the delete operation
type config = Cache_aux.config = {
   mutable max_size : int64; (*The maximum size in bytes*)
   mutable save_cache_period : int; (*Save the cache to disk every this number of seconds. 0 or negative number disables saving to disk entirely*)
   mutable save_cache_speed : int; (*How fast to save to disk in bytes per second. Setting this to a reasonable value increases the responsiveness of the cache server while a save operation is in progress. Setting to 0 means as fast as possible.*)
}
Settings for the cache server
type stats = Cache_aux.stats = {
   mutable num_entries : int; (*The number of entries in the cache, including entries scheduled for deletion.*)
   mutable num_bytes : int64; (*The size of the cache in bytes, excluding overhead space*)
   mutable num_calls_get : int; (*The number of get calls*)
   mutable num_calls_set : int; (*The number of set calls*)
   mutable num_calls_delete : int; (*The number of delete calls*)
   mutable num_hits : int; (*The number of get calls returning `Found.*)
   mutable counters_reset_on : timestamp; (*When the counters were last reset*)
}
Statistics of a cache server
type 'a async_reply = (unit -> 'a) -> unit 
Result type of an asynchronous call. A typical example of such a callback function is
 let process_result get_result =
      try 
        let result = get_result() in  (* May raise exception *)
        ...
      with error -> ...
 

exception Server_not_alive
Raised by RPC calls if the config returns false for the is_alive test.
class type client_config = object .. end
Configures the RPC client.
class type async_client = object .. end
Client access to cache by asynchronous RPC calls
class type sync_client = object .. end
Client access to cache by synchronous RPC calls
val create_async_client : client_config ->
Unixqueue.event_system -> async_client
Create an asynchronous client for the passed config and the passed event system. Note that you must Unixqueue.run the event system in order to conduct the requested operations.
val create_sync_client : client_config -> sync_client
Create a synchronous client for the passed config
This web site is published by Informatikbüro Gerd Stolpmann
Powered by Caml