This is nscache version @VER@.
AOLserver implements a C API for caching arbitrary data. This
module provides a Tcl API on top of the C API. The module is
only compatible with nsd8x
, not nsd76
.
A cache, in this context, is simply a dictionary that maps keys to values. Keys are always stored as NUL-terminated strings. How values are stored depends on the type of cache.
ns_cache create cachename
?-size maxsize? ?-timeout timeout?
?-thread thread?
ns_cache eval cachename key
script
ns_cache flush cachename key
ns_cache get cachename key
?varname?
ns_cache names cachename
ns_cache set cachename key value
ns_cache create cachename -size maxsize
Entries in a cache of this type are accessible to all threads. Each cache has its own mutex that protects access to its entries.
Cache values are stored as counted strings, so arbitrary binary data can be cached. A global cache stores strings instead of Tcl objects to prevent race conditions that could lead to heap corruption.
The cache has a maximum size specified when the cache is created. The size of the cache is the sum of the sizes of all the values in the cache; keys do not count toward a cache's size. If inserting a value into the cache makes the cache's size exceed its maximum, then cache entries are evicted starting with the least-recently used entry until the size is below the maximum size (or until only the new value remains in the cache).
ns_cache create cachename -timeout timeout
Entries in a cache of this type are accessible to all threads. Each cache has its own mutex that protects access to its entries.
Cache values are stored as counted strings, as in a global size-limited cache.
The cache has a maximum entry lifetime, called its timeout, specified (in seconds) when the cache is created. Every timeout seconds, AOLserver flushes all cache entries that have not were not created or accessed in the last timeout seconds.
ns_cache create cachename -size maxsize -thread 1
Each thread in AOLserver automatically gets its own private cache named cachename. Since a thread-private cache is only accessed by one thread, access to it does not require a mutex. Entries in one thread's cache are not visible to any other thread.
Cache values are stored as Tcl objects. When a value is stored in the cache, nscache computes its string form and uses the length of the string as the size of the value.
The cache has a maximum size, like a global size-limited cache. However, because of the way the cache value sizes are computed, the actual memory usage of the cache values may be several times larger than maxsize.
Thread-private caches may offer higher performance if the cached values are complex objects such as lists or scripts, but require more storage than global caches.
ns_cache create cachename
?-size maxsize? ?-timeout timeout?
?-thread thread?
This command creates a new cache named cachename. If thread is given and is true, then it is a thread-private cache. Otherwise it is a global cache. If maxsize is given, then it is a sized-based cache. Otherwise, if timeout is given, then it is a timeout-based cache. Otherwise, it is a timeout-based cache with an infinite timeout.
This command returns nothing if it is successful.
ns_cache eval cachename key
script
This command atomically sets and gets a cache value. First, it looks up key in the cache named cachename. If it finds an entry, it returns the value of that entry. Otherwise, it executes script, stores the return value in the cache, and also returns that value.
Script may optionally use the return
command to return its value. For example, this will store the
value "2" in mycache, if mykey is not already present:
ns_cache eval mycache mykey { expr {1+1} }
This will also store the value "2" in mycache:
ns_cache eval mycache mykey { return [expr {1+1}] }
If script raises an error, or exits with
break
or continue
, then ns_cache
eval
simply returns the same condition without modifying
the cache.
This command is particularly useful for global caches because of its atomicity. Consider this definition:
proc get_thing {key} { ns_cache eval thing_cache $key { # some long, expensive database operation that # computes the value for $key } }
Suppose thread 1 calls get_thing A
, and
A
is not found in thing_cache
. Thread
1 begins the long operation to compute the value for
A
. Meanwhile, thread 2 calls get_thing
A
. Thread 2 will not try to compute the value right
away. Instead, it will block because thread 1 is already
computing the value. When thread 1 finishes and stores the
value in the cache, thread 2 will then pull the value out of the
cache. Meanwhile, if thread 3 calls get_thing B
,
it proceeds independently from threads 1 and 2. Here is a
diagram:
Thread 1 | Thread 2 | Thread 3 |
---|---|---|
calls get_thing A |
||
calls ns_cache eval |
||
begins long operation for A | ||
(computing...) | calls get_thing A |
calls get_thing B |
(computing...) | calls ns_cache eval |
calls ns_cache eval |
(computing...) | waits for thread 1 to finish | begins long operation for B |
(computing...) | (waiting...) | (computing...) |
long operation returns value for A | (waiting...) | (computing...) |
ns_cache eval stores and returns value |
(waiting...) | (computing...) |
get_thing A returns value |
ns_cache eval returns value |
(computing...) |
get_thing A returns value |
long operation returns value for B | |
ns_cache eval stores and returns value |
||
get_thing B returns value |
ns_cache flush cachename key
This command removes the entry for key from the cache named cachename. If the cache has no entry for key, then nothing happens.
For global caches, ns_cache flush
interacts
with ns_cache eval
. Suppose thread 1 has called
get_thing A
and is executing the long operation to
compute the value for A. Thread 2 calls get_thing A
and
starts waiting for thread 1 to finish. Thread 3 calls ns_cache
flush thing_cache A
. Thread 1 will continue executing the long
operation, but thread 2 will also start the long operation. When
thread 1 completes the long operation, ns_cache eval
returns the (now stale) value it computed, but it does not
store the value in the cache. When thread 2 completes the long
operation, ns_cache eval
stores the (fresh) value it
computed in the cache and returns the fresh value. Here is a diagram:
Thread 1 | Thread 2 | Thread 3 |
---|---|---|
calls get_thing A |
||
calls ns_cache eval |
||
begins long operation for A | ||
(computing...) | calls get_thing A |
|
(computing...) | calls ns_cache eval |
|
(computing...) | waits for thread 1 to finish | |
(computing...) | (waiting...) | calls ns_cache flush thing_cache A |
(computing...) | begins long operation for A | |
long operation returns stale value for A | (computing...) | |
ns_cache eval returns stale value |
(computing...) | |
get_thing A returns stale value |
long operation returns fresh value for A | |
ns_cache eval stores and returns fresh value |
||
get_thing A returns fresh value |
ns_cache get cachename key
?varname?
This command looks up key in the specified cache. It operates differently depending on whether varname was given.
Varname Not Given | Varname Given | |
---|---|---|
Key Not Found | Raises error | Returns "0" |
Key Found | Returns value | Sets varname to value and returns "1" |
If some other thread is in ns_cache eval
when
ns_cache get
is called for the same global cache and key,
then ns_cache get
waits for the other thread to finish.
ns_cache names cachename
This command returns a list of all keys currently in the specified cache.
If the cache is thread-private, then the list only includes keys that are in the thread's private cache.
ns_cache set cachename key value
This command stores value for key in the specified cache. It is roughly equivalent to this script:
However, thens_cache flush cachename key ns_cache eval cachename key { return value }
ns_cache set
command operates
atomically.
nscache-@VER@
. Change into that directory
and run make. Be sure to tell make the path to your AOLserver
installation. For example, if your nsd8x executable is
/usr/local/aolserver/bin/nsd8x
, then use these commands:
The module should compile with no errors. You may optionally run the test cases:tar xvzf nscache-@VER@.tar.gz cd nscache-@VER@ make INST=/usr/local/aolserver
Examine the output for lines containing the string "test outcome". If any of those lines say "failure", something went wrong.make test INST=/usr/local/aolserver
Next, install the module.
make install INST=/usr/local/aolserver
To enable the module in your server, edit your
nsd.tcl
or nsd.ini
. For
nsd.tcl
, add these lines:
Use your own server name in place of server1. Forns_section ns/server/server1 ns_param nscache nscache.so
nsd.ini
, add these lines:
Restart your server after saving the configuration file. After you have enabled the module, your Tcl scripts have access to the[ns/server/server1] nscache=nscache.so
ns_cache
command.