# SHORE Storage Manager Application Programming Interface (SSM API)

Collaboration diagram for SHORE Storage Manager Application Programming Interface (SSM API):

## Introduction

Most of the SHORE Storage Manager functionality is presented to the user (server-writer) in two C++ classes, ss_m and smthread_t. The ss_m is the SHORE storage manager, a single instance of which must be constructed before any storage manager methods may be used. There cannot be more than one ss_m instance extant. The construction of the single instance performs recovery. Deletion of the single instance shuts down the storage manager.

The storage manager stores state information in per-thread variables; for this reason, storage manager methods must be called in the context of a storage manager thread, smthread_t. This means they must be called (directly or indirectly) by the run() method of a class derived from smthread_t. As a result, you must write a class to encapsulate your server functionality, and that class must derive from smthread_t, as described in the following pseudo-code:

main()
{
// Marshall all the resources needed to get going:
// "Open" the sources of work for your server, such as
// listening on a network socket, or opening an input
//
// Start up a storage manager:
// let the server thread do its thing
server->fork();
// wait until it's done
server->join();
// clean up
delete server;
// un-marshall resources (close files, clean up, etc)
}

// This class creates and destroys a storage manager,
// and forks the worker threads that use the storage manager.
{
void run(); // this is virtual in smthread_t
};

// This class performs work given to it by a source.
// It uses the given storage manager instance to perform that work.

{
your_worker_thread_t (ss_m *ssm, ...source... ) ;
void run(); // this waits for work from its assigned source
// (e.g., from terminal input or from a network connection), and
// performs the necessary work
};

{
// marshal resources neeeded for storage manager work
// including run-time options (see discussion below).
ss_m *ssm = new ss_m(...);

// Fork off some number of threads
// worker threads that use the instance ssm.
// Either pass ssm to these threads' constructors or
// make ssm global.
for(int i = 0; i < num_threads; i++) {
workers[i] = new your_worker_t(ssm, ... source ... );
}

for(int i = 0; i < NUM_THREADS; i++) {
workers[i]->fork();
}

// run() method returns.
for(int i = 0; i < NUM_THREADS; i++) {
workers[i]->join();
}
for(int i = 0; i < NUM_THREADS; i++) {
delete workers[i];
}

delete ssm;
// un-marshal (clean up) resources neeeded for storage manager work
} // a join() on this thread now returns.

* 

The storage manager relies heavily on certain programming idioms to make sure return values from all methods are checked. The idioms are encapsulated in preprocessor macros. As a user of the storage manager, you strongly encouraged to use these idioms. Although the use of the macros is optional, perusal of the storage manager source code and examples will be easier if you are aware of them. They are described in Programming Idioms, Significant C Preprocessor Macros, and w_rc.h. Before you spend much time looking at examples, it would be worthwhile to look at the macro definitions in w_rc.h.

The storage manager is parameterized with options and their associated values. The options determine such things as the size of the buffer pool and lock table, the location of the log, lock granularity, certain caching behavior. Most of these have default values, but some options (such as a path name indicating the location of the log) do not have a default value and must be given values at run time.

An options-processing package is provided for this purpose. It handles the parsing of option names and values, which may be given on the command line, or in a file or input stream. Because certain options must be given a value at run-time, the use of this package is not optional: every server must at least create a minimal set of options and give them values. In the above pseudo-code, invoking the run-time options package would be inserted in

 your_server_thread_t::run()

or in
 main().


The storage manager release comes with small examples illustrating how the options are to be used.

See

With few exceptions, the storage manager does work on behalf of a transaction and the storage manager acquires locks for that transaction, e.g., to read a record, the storage manager acquires read locks and to update a record it acquires write locks. Rather than expect the transaction of interest to be given as an argument to every storage manager method, the storage manager assumes an attachment between a storage manager thread and a transaction. The attachment is not fixed and permanent; rather, a worker thread can choose which transaction it is serving, and can set aside transaction A and proceed to serve transaction B, while another thread can pick up transaction B and do work on its behalf. A thread cannot serve more than one transaction at any time, and, except under limited circumstances, a transaction cannot be served by more than one thread at a time. Through the API, a storage manager thread can :

• start a new transaction (implictly attaching the transaction to the thread)
• detach the attached transaction
• attach an arbitrary transaction
• perform work on behalf of the attached transaction
• prepare the attached transaction
• commit or abort the attached transaction (implicitly detaching it)

See Transactions, Locking and Logging for details.

Persistent data are contained in a variety of storage structures (files of records, indexes, etc.). All data structures reside in volumes, which are mounted. Identifiers for data on the volume contain that volume number. The act of mounting a volume creates the association of the volume number with the path name of a Unix file. OK, that's a lie. The original design of SHORE called for multiple volumes per Unix file, and so the file was associated with a device, and volumes were contained in a device. SHORE never supported multiple volumes on a device, but the device-volume distinction remains. Thus, a server mounts (and formats, if necessary) a device, which is identified by a path name. Then the server creates a volume (numbered), which resides on the device.

See Storage Structures for details about storage structures, and see Identifiers for a description of the identifiers used for storage structures and transactions.

## Compiling and Linking Server Code

The compiler invocation requires certain flags to ensure the use of pthreads and the LP64 data model; it also has to include the storage manager's libraries.

When the storage manager is built, its "make" flags are written to a file called

• "makeflags" in the source root directory (or the installed include/ directory). These flags usually include (subject to platform):
• -DARCH_LP64 -m64

The list of libraries must include -lrt and possibly -lnsl.

## Examples

Any code that uses the SHORE Storage Manager requires
 #include <sm_vas.h>


### A Minimal Example

For a simple example, see startstop::cpp

### Setting Up Run-Time Options

The example init_config_options::cpp demonstrates a more extensive handling of run-time options, and is used in other examples, below.

### Creating a file of Records

The example create_rec::cpp shows a server that creates a file of records. Thus, it also contains code to initialize a volume and create a file.

### Use of ss_m::ss_m Arguments

The example log_exceed::cpp demonstrates the use of the arguments to the storage manager constructor (ss_m::ss_m). It is an extension of the above example that generates enough log to run out of log space.
Generated on Mon Jan 2 15:14:00 2012 for Shore Storage Manager by  1.4.7