BACKGROUND
A server that accepts incoming tcp or unix connections, and gets/sets UniConf elements at the request of a UniConfClient.Note that if it's designed correctly, UniConf should be able to work just fine (even with multiple users of a single config file) without needing a daemon; we can just have it rescan the config file every now and then, maybe using something like the FileAccessMonitor to speed up notifications. But the daemon would let us get individual notifications when a particular key/subtree changes, which is harder on a plain filesystem - at least until ReiserFS? changes everything :)
PROTOCOL
==> GET 'key':
if 'key' is found
<== VALUE 'value'
<== OK
otherwise
<== FAIL
==> SET 'key' 'value'
key is created or its value is changed
<== OK / FAIL
==> DEL 'key'
key is deleted
<== OK / FAIL
==> ZAP 'key'
key's children are deleted
<== OK / FAIL
==> SUBT 'key'
for each child of 'key'
<== VALUE 'childkey' 'value'
<== OK
==> QUIT
connection is closed cleanly
<== OK
==> UNKNOWN_COMMAND
<== FAIL
==> ADDWATCH 'key' 'depth'
watch for 'key' at 'depth' is added
where 'depth' is one of:
'zero', 'one', 'infinite', 'children', 'descendents'
<== OK
==> DELWATCH 'key' 'depth'
watch for 'key' at 'depth' is deleted
<== OK
==> HELP
<== TEXT some help text
<== TEXT more help text
<== OK
==> (unsolicited)
sent at start of session
<== HELLO server information
==> (unsolicited)
sent whenever a watch is triggered
<== CHG 'key' 'depth'
TROUBLESHOOTING
Probably the best way to troubleshoot the daemon is to connect directly to it with netcat or telnet:
drheld@spork:~ $ nc localhost 4111
HELLO {UniConf Server ready}
help
TEXT {noop: verify that the connection is active}
TEXT {get : get the value of a key}
TEXT {set : sets the value of a key}
TEXT {del : deletes the key}
TEXT {subt : enumerates the children of a key}
TEXT {hchild : returns whether a key has children}
TEXT {quit: kills the session nicely}
TEXT {help: returns this help text}
TEXT {OK : reply on command success}
TEXT {FAIL : reply on command failure}
TEXT {CHILD TRUE / FALSE: key has children or not}
TEXT {ONEVAL : reply to a get}
TEXT {VAL : intermediate reply value of a key}
TEXT {TEXT : intermediate reply of a text message}
TEXT {HELLO : sent by server on connection}
TEXT {NOTICE : forget key and its children}
OK
LONG TERM PLANS and DESIGN CONSIDERATIONS
jbrown (2003/01/21): Problems with the current protocol:
- lots of round-trips
- no support for batching commands
- watches are not identified by a token so it is possible to get confused about which should be added/deleted
- very often a GET is followed by SUBT to look for children
- CHG message does not indicate the type of change
- GET may retrieve more data than strictly necessary
Proposed solutions:
- change protocol to use key patterns instead of keys
- eliminate SUBT because it is obviated by GET with a pattern
- eliminate ZAP because it is obviated by DEL with a pattern
- add a
tokenfield to ADDWATCH / DELWATCH - add a
change typefield to CHG event to distinguish between creation, removal, and property change of keys - extend GET / SET / VALUE to manipulate key properties in a structured manner (more later)
- eliminate DEL because it is obviated by SET with key existance property set to false
Key properties:
-
vvalue: value associated with key, undefined if key does not exist -
xexistance: 1 if key exists, 0 otherwise -
lleaf: 1 if key is a leaf, 0 otherwise -
ppermissions: possible access permissions (someday...) -
iimmutable: 1 if key is read-only, 0 otherwise (someday...) - etc...
Motivation for introducing a batch-property GET/SET:
- To eliminate some GET / SUBT pairs that often occur during recursive traversal, it would be sufficient for GET to return information about whether the key has any children or not along with the value. This is a much cheaper alternative for caching than actually prefetching those children.
- UniConf will likely be extended in a variety of ways to hold additional meta-data about its keys. We would like a cheap and extensible mechanism for doing this without introducing extra round-trips.
- Depending on the context, it is not always useful to query all of the information in a UniConf tree at once. The client should be able to query data on demand incrementally at its discretion.
Suggested protocol changes:
==> GET 'pattern' {'p1' 'p2' ...}
for each key matching 'pattern'
<== INFO 'key' {{'p1' 'v1'} {'p2' 'v2'} ...}
where {'p1' 'v1'} and {'p2' 'v2'} form named property pairs
<== OK / FAIL
where FAIL is only returned if something nasty happens rather
than on key non-existance
==> SET 'pattern' {{'p1' 'v1'} {'p2' 'v2'} ...}
if pattern is wild then for each key matching 'pattern'
sets property 'p1' to 'v1' and 'p2' to 'v2'
else
creates key 'pattern' with initially empty value and sets
properties as specified
<== OK / FAIL
Eliminate DEL, ZAP and SUBT because:
DEL 'key' becomes: SET 'key' {{x 0}}
ZAP 'key' becomes: SET 'key'/* {{x 0}}
SUBT 'key' becomes: GET 'key'/* {v}
RSUB 'key' becomes: GET 'key'/*/... {v}
(recursive subtree list was present in earlier versions)
==> ADDWATCH 'key' 'depth' 'token'
watch for 'key' at 'depth' is added with 'token' as name
where 'depth' is one of:
'zero', 'one', 'infinite', 'children', 'descendents'
<== OK
==> DELWATCH 'key' 'depth' 'token'
watch for 'key' at 'depth' is deleted with 'token' as name
<== OK
==> (unsolicited)
sent whenever a watch is triggered
<== CHG 'key' 'depth' 'type'
where 'type' is one of:
'add', 'del', 'change'
WHY DOES UniClientGen? IMPLEMENT IT'S OWN TCP: and UDP: MONIKERS...
Instead of just taking "client:tcp:..." or "client:udp:..."? (Right now there's a "wvstream:..." that takes any WvStream? moniker - why not just use this all the time?)It has its own monikers so that they can include uniconf-specific default port numbers and such. At the time this was first written, UniConf was not sharing the same moniker namespace as the rest of WvStreams.
- apenwarr (2003/03/22): in fact they are two separate moniker namespaces; one returns IWvStream? objects, and another returns UniGen? objects. However, the
wvstream:moniker in the UniGen? namespace drops into the IWvStream? namespace and wraps a client around it, which is pretty clever if I do say so myself. That said, the UniConfGentcp:andudp:monikers are basically just fluff that allows you to type less to initialize a UniConf object. It's arguable that they should go away, but also arguable that they were almost no work. :)- jnc (2003/03/24): More importantly than typing less, they allow you to not have to look up the default port. Looking stuff up is way more annoying than typing.
jdeboer (2004/09/29): Methinks this protocol description is a wee bit outdated. (Based on wvstreams-3.75)
jdeboer (2004/09/29): Here is a overview of the protocol based on what uniconfd from wvstreams-3.75 actually does:
==> GET 'fullkey' if key is found <== ONEVAL 'fullkey' 'value' else <== FAIL ==> SET 'fullkey' 'value' (no response from server) ==> DEL 'fullkey' (no response from server) (ZAP does not exist) ==> SUBT 'fullkey' <== VAL 'relativekey' 'value' ... <== OK ==> QUIT <== OK (ADDWATCH does not exist) (DELWATCH does not exist) at start of session <== HELLO server information sent whenever any key changes <== NOTICE 'fullkey' 'value'
Notes:
- the quotes are not included in the commands. Say "SET asheley ast" instead of "SET
ashley'ast'" - NOTICEs are sent whenever a key changes. That includes when you change a key. e.g. using the SET command. Therefore it will look like NOTICE is the response to SET, but it is not.