RDBM (Remote DBM) converts a DBM-using program to an RPC Client-Server architecture. The benefits include networked access (and avoidance of NFS), and the elimination of the usual DBM locking problems in an asynchronous environment such as a multi-user system.
The origins of RDBM go back to a project "ODAS" - Optical Disc Archive System - software to manage a multi-terabyte archive of satellite image data using robotic storage hardware. This was implemented on SunOS 4.1, and used NDBM for a fast online index of all products available in the archive. To allow multiple processes concurrent access without a complex locking scheme and the perils of NFS, I made the DBM a server. It has been operational with zero error rate since 1993, and is now incorporated in the backend of several systems, including data chains and webservers.
It became clear from ODAS that the RPC architecture was an excellent way to go when using a DBM, and I have used it in several later systems. RDBM attempts to capture the essence of these in a self-contained package.
RDBM is simply an implementation of the BASE_DBM interface, in which the constructor and destructor respectively open and close a connection to the Server. The actual code to manage the Client-Server interface is generated by rpcgen from a source file rdbm.x.
AnyDBM includes Servers for RDBM - one for each of the DBMs supported (they may not all be available on your system - just pick one that works). RDBM Clients will need to link in librdbm - which is directly analagous to linking libdbm, libgdbm or libdb. For further enlightenment, see how the example program is compiled both to a standalone DBM program and to an RDBM Client program in the Makefile.
Unlike the other implementations of BASE_DBM, RDBM is not built on an existing library for C programmers. An additional interface, similar to the other DBMs, is therefore also provided:
#ifdef RDBM_CC_H
/* The implementation is compiled as C++ and needs the functions of an RDBM */
typedef RDBM<const char*>* rdbm_fd ;
#else
/* to C programs this is just a handle - they don't need the details */
typedef void* rdbm_fd ;
#endif
/* rdbm_open() is a constructor. Always check errnum(), and rdbm_close()
it when done even if the rdbm_open() failed
*/
rdbm_fd rdbm_open(const char* host, const int prog, const int vers) ;
void rdbm_close(rdbm_fd fd) ;
/* zero for success - else errnum() */
int rdbm_reconnect(rdbm_fd fd, const char* host, const int prog, const int vers);
int rdbm_put_new(rdbm_fd fd, const char* key, const char* val) ;
int rdbm_put(rdbm_fd fd, const char* key, const char* val) ;
int rdbm_del(rdbm_fd fd, const char* key) ;
int rdbm_fetch(rdbm_fd fd, const char* key) ;
int rdbm_nextkey(rdbm_fd fd, const char* key) ;
int rdbm_firstkey(rdbm_fd fd) ;
/* zero for failure - check errnum() */
const char* rdbm_get(rdbm_fd fd, const char* key) ;
const char* rdbm_seq(rdbm_fd fd, const char* key) ;
int rdbm_errnum(rdbm_fd fd) ;
void rdbm_clearerr(rdbm_fd fd) ;
const char* rdbm_errmsg(rdbm_fd fd) ;
const char* rdbm_lastval(rdbm_fd fd) ;
Note that when using the C interface, you should explicitly call rdbm_close when done - you don't automatically have the benefit of a destructor to take care of that for you.