The CogServer is a network server for the AtomSpace. It provides three different services, tied fairly closely together. These are a telnet server to an OpenCog command line, a telnet server to scheme and python command lines, (which can be used at the same time, for the same AtomSpace!) and a high-performance Atomese network server. It is straight-forward to extend the CogServer with network services of this kind.
Most of this wiki page will be devoted to describing the high-performance network server. It is fairly important to understand, as it provides a basic building block for building a distributed, decentralized AtomSpace. It provides the basic peer-to-peer networking layer, by means of the CogStorageNode.
Some comments about network serving in general, including other systems, such as RESTful API's, are given at the bottom of this page.
The components are:
- A telnet server providing access to a command-line shell. A plugin system allows new commands to be added in a pluggable manner. This shell is mostly unused at this time; it does offer a command-line interface to the AgentServer.
- A telnet server providing multi-user access to a scheme REPL shell, and a python REPL shell. (REPL stands for "Read Evaluate Print Loop"). These shells are very useful for starting, stopping and managing long-running AtomSpace jobs. For example, one terminal can be used to start a big job, while a second terminal can be used to monitor its progress. Both the scheme and the python shells can be used at the same time, so that, for example, an Atom created with Python is instantly visible to scheme, and vice-versa. This is particularly useful when interfacing to ROS (Robot Operating System), which is primarily developed in python. The scheme shell is particularly efficient for moving Atoms across the network.
- A high-speed Atomese server. This is a small subset of the scheme shell, but very highly tuned for maximum performance for moving around Atoms on the network. We believe that it is (very nearly) impossible to beat the performance of this component, using any other technology, including ZeroMQ, RESTful API's, Protocol Buffers, JSON servers, and so on. The reason for this is that Atomese is easy to decode, as it has a regular structure: it can be done with string compares. Running Atomese is also extremely fast: it runs at the speed of C++ constructors. There is very little overhead, almost nothing to trim.
The high-speed Atomese server has been used to implement the CogStorageNode, which uses the CogServer to move Atoms around on the network, point-to-point, from within the AtomSpace. A conventional Atomspace job can use the CogStorageNode to easily fetch, send and query Atoms (the full query mechanism-- BindLink and friends, are supported. Basically, you can run BindLink over the network.) The protocol for accomplishing this is documented below.
The source code for the CogServer can be found in the git repo here: https://github.com/opencog/cogserver
Additional shells (i.e. shells for things other than python or scheme) can be implemented by modelling existing code in this directory: https://github.com/opencog/cogserver/opencog/cogserver/shell
Using the CogServer
Please refer to the OpenCog shell page for general info about how to start the CogServer, and how to connect to the telnet shell, and thence the python or scheme shells. The same menu also provides the sexpr shell, descried below.
High-performance Atomese serving
This section reviews the so-called sexpr shell: this shell handles a small handful of special commands that generally take Atomese s-expressions as arguments. A few also take #t and #f as booleans, emulating the scheme API for these same functions.
Atomese and S-Expressions
(EvaluationLink (PredicateNode "enjoys") (ListLink (Concept "Linas") (Concept "being outdoors")))
Such expressions can be parsed easily enough by performing string searches. Parsing such expressions is not quite as fast as some binary format, but they are eminently human-readable, making them quite nice for general code development, ad hoc usage, debugging. They are easy to print. Most importantly, they can be processed on the server side with relative ease. They can certainly be process much faster than what the guile shell could ever go, or what an equivalent python shell could do.
The sexpr shell
The sexpr can be accessed via telnet, for a manual session, or directly via TCP/IP sockets, for programmatic use. In both cases, the access method is exactly the same: connect to the desired hostname (tcpip address) and port number 17001. (This port number is dynamically configurable, at the time that the CogServer is started.) Enter the sexpr shell by sending the ascii (utf-8) string "sexpr\n" (without the quotes; the \n is the newline character.) After sending this string, the socket will be in sexpr mode. Nothing will be sent back, unless one of the sexpr commands generates output. The sexpr can be gracefully exited by sending a control-D (hex 0x04) or a single '.' on a line by itself (that is, a period (0x2E) followed by a newline (0x0A).
The sexpr commands
The following commands are available. They behave more-or-less exactly the same as their ordinary scheme counterparts. Therefore, using the ,describe command at the guile prompt will provide documentation.
Again, many of these commands are relatively silent; they will not return any bytes, if they don't need to. Syntax errors can be discovered by doing tail -f /tmp/cogserver.log.
At this time, these are the only supported commends.
sexpr usage examples
To transmit a single Atom to the cogserver, use the cog-set-tv! command. For example:
(cog-set-tv! (ConceptNode "foo") (SimpleTruthValue 0.3 0.6))
To list the Atoms in the AtomSpace:
(cog-get-atoms 'Atom #t)
To list only the ConceptNodes:
(cog-get-atoms 'ConceptNode #t)
More complex data transfers can be accomplished by combining these primitives.
Please note that the cog-execute-cache! command is rather dangerous: it will execute any executable Atom. The result of execution is cached at the provided key; this result can be fetched by using cog-value with the same atom and key.
The sexpr implementation
The implementation of these commands is not in the CogServer, but in the base Atomsapce git repo. The actual decoding can be found here: opencog/persist/sexpr/Commands.cc. That is, the sexpr shell is just a network shell. The cogserver hands off the actuall shell processing to the shell implementation, which, in this case, is in opencog/persist/sexpr.
Note what this implies: if you want to create an even faster, niftier, more feature-rich, super-duper network protocol for the AtomSpace, you just have to copy these files, and change things around as you wish.
One can get network shell access in other ways besides the CogServer. For example, guile offers the `(system repl server)` module, documented here, in the guile documentation. Our experience is that it is both slow, and crashy. It is more than ten times slower that the CogServer-provided REPL shell, which might not matter much for typing, but it hurts when sending a lot of data. (Note that the Atomese shell is yet another order of magnitude faster than the Cogserver guile shell.) Much much worse, the guile network shell is prone to crashes and hangs. This is infrequent: maybe once every few hours, but is completely deadly for long-running jobs.
Presumably, there are python network servers as well. We don't know of any and haven't tried them.
Network serving ruminations
One might hope that there exists some off-the-shelf, general-purpose, multi-featured network server. Surprisingly, there does not seem any such (after a brief search; did I miss it?) A generic network server might be expected to provide:
- Authentication, using login credentials of some sort, or possibly via capabilities.
- Encrypted messaging.
- A command dispatcher, with per-command authentication.
From what I can tell, people *do* build these kinds of systems, but they are all web-based. A typical example might be python flask, which is a micro webserver. It provides basic networking. On top of this, there are dozens and dozens of modules providing various services for flask. Sounds great, except this is all layered on top of the HTTP/1.1 protocol. Which is fine, if you are transferring huge things around, like webpages. However, Atoms are tiny: just hundreds of bytes (or less!) -- they are smaller, and sometimes much smaller than the HTTP headers. The CPU overhead of processing HTTP headers is a zillion times larger, longer, heavier, than the CPU time spent processing an Atom. The overhead of processing an HTTP header, (needed for a RESTful interface) becomes not just a bottleneck, it becomes a boat-anchor that is larger than the boat.