Tutorial - rings
Rings are new datatype on version 0.99. Ring is techincally circular double linked datatype with defined current (active) position. Rings use same path semantics than other scache functionality. Every path can contain a ring again containing zero or more items.
By definition rings don't have begin or end, but scache defines start of the ring to be pointer to current position of ring, while end of the ring being position just before current position.
Rings support following operations :
- Pushing data to end of ring by function scache_rnpush. Pushing doesn't affect rings current position.
- Popping data from end of ring by function scache_rnpop. Popping data removes and returns last element of ring without affecting current position.
- Inserting data to begin of ring by function scache_rnunshift. After unshift new value will be the current element while previous current element being second one in ring.
- Popping data from being of ring by function scache_rnshift. Shifting removes and returns current element causing previous second element to be new current one.
- Polling data from current position by function scache_rnget. Polling data does not remove data from ring or change current position of ring.
- Rotate ring with functions scache_rnrotf and scache_rnrotb. Rotating steps current position specified steps either forward or backward.
- Clearing and querying ring's size with functions scache_rnclear and scache_rnsize.html
With functions above other datatype like queues and stacks (FIFOs and LIFOs) can be quite straighforwardly implemented.
Main uses for rings are work queues where clients push data to queue to be further processed and consumed by some dedicated server process. If you are going to implement work queue please ensure to have reliable consumer emptying queue to not risk exhausting the memory.
Examples
Enumerating through ring :
/* purge possible pre-existing */ scache_rnclear($sess, 'queue'); /* populate ring */ scache_rnpush($sess, 'queue', 1); // as first element scache_rnpush($sess, 'queue', 2); // as first element scache_rnpush($sess, 'queue', 3); // as first element for($i = 0, $count = scache_rnsize($sess, 'queue'); $i < $count; $i++) $val = scache_rnget($sess, 'queue');
Polling other positions than current position can be done with scache_iov. Please keep in mind, that rotation and querying with separate functions contain race condition in possibility of other separate clients accessing and rotating same ring between your client's accesses. Therefore combined rotation and querying must be done through scache_iov.
function scache_rngetlast($sess, $path) { list($dummy1, $val, $dummy2) = scache_iov($sess, Array(Array(SCIOP_RNROTB, $path, 1), Array(SCIOP_RNGET, $path), Array(SCIOP_RNROTF, $path, 1))); return $val; } /* purge possible pre-existing */ scache_rnclear($sess, 'queue'); /* populate ring */ scache_rnpush($sess, 'queue', 1); // as first element scache_rnpush($sess, 'queue', 2); // as first element scache_rnpush($sess, 'queue', 3); // as first element echo "Last-1: " . scache_rngetlast($sess, 'queue') . "\n"; // displays 3 echo "Last-2: " . scache_rngetlast($sess, 'queue') . "\n"; // displays 3 ?>
Misc notes
Rings are enabled by default, but disabling them by server config should be considered on semi-hostile environment.
Rings are persistent data storage, so everything pushed to rings will be there until someone purges them. There is of cource memory limitter to prevent rings from hogging all system resources, but once ring hits on limit nothing can be pushed there until some data is purged to make space. This leaves door open for denial of service attack.
To make recovering harder there is no scache_shcoll like scache_rncoll() function to enumerate over rings to kill hostile one. This leaves very little options for graceful recovery.
If you are going to build workqueue like functionality assumption is that you keep good track where they are. Put your rings at least under some subdirectory to get rid of them without having to kill whole scached daemon.