Tutorial - operations

Commands to operate on stored data are storing values, fetching values and testing for values. Operationally identical commands are available for both session private and shared keyspace.

Storing values

Storing operations require valid connection, key (path) to where to store and the value itself. If value is not string it will be internally serialized before transmission. For storing there is three different store commands that differ from what to do if there exists previous value on same key.

$conn = scache_reset('Sessid');

/* setting sets value regardless whether it previously exists or not */
scache_set($conn, 'set-1', 'value'); // returns true

/* adding won't replace existing value */
scache_add($conn, 'set-1', 'new-value'); // returns false
scache_add($conn, 'add-1', 'new-value'); // returns true

/* replace replaces only previously existing values */
scache_replace($conn, 'set-1', 'new-value'); // returns true
scache_replace($conn, 'replace-1', 'new-value'); // returns false

For the shared storage there is equal commands but with slightly different prefix.

$conn = scache_reset('Sessid');

scache_shset($conn, 'set-1', 'value'); // returns true
scache_shadd($conn, 'set-1', 'new-value'); // returns false
scache_shadd($conn, 'add-1', 'new-value'); // returns true
scache_shreplace($conn, 'set-1', 'new-value'); // returns true
scache_shreplace($conn, 'replace-1', 'new-value'); // returns false

You can store value to any path at any time regardless whether parenting paths exist. If you store value to already existing path its contents will be replaced. If the path you are storing had subvalues, ie. is directory path, it will be replaced and all its subvalues will be deleted and resources freed on backend.

Fetching values

Data is fetched by :

$value = scache_get($conn, 'path/to/value'); // session private keyspace
$value = scache_shget($conn, 'path/to/value'); // shared keyspace

If value does not exist, both functions return false. If you need to distinguish whether returned false means that value does not exists or stored value itself was actually false, you need to check last error code with scache_lasterr()

if ((($value = scache_get($conn, 'unexistent')) === false) &&
    (scache_lasterr($conn) != SCNODE_VALUE)) echo "Unexistent";

Another way to detect unexisting values is to do it with scache_iov(), althought as from pedantic view it might be some nanoseconds slower than way above :

list($stat, $value) = 
                Array(Array(SCIOP_STAT, 'value'),
                      Array(SCIOP_GET, 'value)));

If you expected iop SCIOP_EXISTS above, there is none. Scache_iov has no iop SCIOP_EXIST. It got me also and I actually did begin to fix it as a astonishing severe bug after my 2 years coding break with this software.

You need to use SCIOP_STAT instead. It returns SCNODE_VALUE if queried path contained value. Single op PHP function scache_exist() is actually just extension's internal wrapper over scache_stat(), not recognized by protocol itself.

Testing for values

You can test for existence of path with scache_exists() from session private keyspace and with scache_shexists() from shared keyspace.

As an example code :

$conn1 = scache_reset('Session-1');
$conn2 = scache_reset('Session-2');

scache_set($conn1, 'path/value1', 'Value 1');
scache_set($conn2, 'path/value2', 'Value 2');
scache_shset($conn1, 'path/shared', 'Shared');

# private values are private
scache_exists($conn1, 'path/value1'); // returns true
scache_exists($conn1, 'path/value2'); // returns false
scache_exists($conn2, 'path/value1'); // returns false
scache_exists($conn2, 'path/value2'); // returns true

# shared values are shared
scache_shexists($conn1, 'path/shared'); // return true
scache_shexists($conn2, 'path/shared'); // return true

# and as a reminder, shared keyspace is different from private
scache_shexists($conn1, 'path/value1'); // return false
scache_shexists($conn2, 'path/value2'); // return false

If you need to query nodetype of path, you can do it with scache_stat() for session private keyspace and with scache_shstat() for shared keyspace. For existing path returned value is either SCNODE_VALUE or SCNODE_BRANCH depending queried path being value or directory containing subvalues.

For unexistent paths returned value is either SCNODE_NONEXISTENT or SCNODE_EMPTY.

Technically while SCNODE_NONEXISTENT means unexisting path, SCNODE_EMPTY is path that has once existed, but is cleared with scache_unset() or scache_shunset(). Both of these means that there is no value and both of these must be regarded as unexisting path.

Internally SCNODE_NONEXISTENT is defined as 0 and SCNODE_EMPTY as value 1, so you can test existence by testing return value being bigger than SCNODE_EMPTY.

Reason for different values for empty path is in scached's internal implementation. When some path is deleted, only its contained data is recursively cleared and node itself is marked as unused, but referring key is left to its containing tree. This is just for to omit re-balancing of b-tree on every value deletion. These leftover keys has understandably some consequences on memory footprint, but these will be described as feature when talked about persistent storage, and as a bug when talked about cache. In both cases those consequences can be overcome with sane data structuring.

There isn't probably any kind of use for different types for unexisting path, but they are still carried in protocol.

$conn = scache_reset('Session-0');

scache_set($conn, 'path/to/value', 'Value');
scache_set($conn, 'path/to/deleted', 'Deleted');
scache_unset($conn, 'path/to/deleted');

scache_stat($conn, 'path/to/unexistent'); // returns SCNODE_UNEXISTENT
scache_stat($conn, 'path/to/deleted'); // returns SCNODE_EMPTY
scache_stat($conn, 'path/to/value'); // returns SCNODE_VALUE
scache_stat($conn, 'path/to'); // returns SCNODE_BRANCH
scache_stat($conn, 'path'); // returns SCNODE_BRANCH

scache_unset($conn, 'path');
scache_stat($conn, 'path/to/deleted'); // returns SCNODE_UNEXISTENT
scache_stat($conn, 'path/to'); // returns SCNODE_UNEXISTENT

Listing keys

Subkeys of some parent node can be queried with scache_coll and scache_shcoll. When invoked, both functions list nonrecursively all immediate subkeys of given path and store result to location given in third argument.

Note that these functions does not return the listing of keys on given path, but stores that listing to eligible location from where it is to be fetched on separate query.

This approach makes possible to avoid race condition that would happen if listing is fetched to client and immediately pushed back to be shared with other clients.

As its simples form, there is path containing subvalues :

scache_set($conn, 'subdir/value-1', true);
scache_set($conn, 'subdir/value-2', true);
scache_set($conn, 'subdir/value-3', true);

Path /subdir is listed and result stored into path /listing

scache_coll($conn, 'subdir', 'listing');

...from where it is fetched with another query.

$listing = scache_get($conn, 'listing');

Returned listing in PHP array (map) where array's keys represent subkeys found and array's values are nodetypes of the key.

$listing = scache_get($conn, 'listing');
foreach($listing as $key => $nodetype) 
  echo "Key : $key, nodetype: " .
   (($nodetype == SCNODE_VALUE) ? 'NODE' : 'SUBDIR') . "\n";

For race condition free operations you need to do index-keeping by operating through scache_iov -interface.

list($addok, $collok, $listing) =
   scache_iov($conn, Array(Array(SCIOP_ADD, 'subdir/new-value', 'val'),
                           Array(SCIOP_COLL, 'subdir', 'listing'),
                           Array(SCIOP_GET, 'listing')));