version: 0.2.0
| tag: v0.2.0
| commit: fb3bfde19
@sabl/storage-pool
storage-pool is a simple, context-aware pattern for describing connection pooling and storage transactions agnostic of the underlying storage type.
The interfaces defined in this package set consistent expectations for how storage services should expose basic pool and connection types. They are based on the connection lifecycle APIs in the go standard library database/sql
package. One minor difference is the that commit
and rollback
methods of the Txn
interface accept a context.
For more detail on the storage-pool pattern, see sabl / patterns / storage-pool.
@sabl/txn
Compatibility with This package does not depend on @sabl/txn
, but the StorageTxn
type defined here is compatible with the Txn
interface in @sabl/txn
, and StoragePool
and StorageConn
are compatible with the Transactable
interface in @sabl/txn
.
API
StorageAPI
An abstraction of all storage API types, exposing two enumeration properties that provide some clues about the type of the API instance.
export interface StorageApi {
readonly mode: StorageMode;
readonly kind: StorageKind | string;
}
StorageMode
Represents the basic type of the API instance: pool, connection, or transaction.
export enum StorageMode {
pool = 1,
conn = 2,
txn = 3,
}
StorageKind
Extensible string enumeration describing the basic underlying storage type, such as relational, document, graph, etc. Authors may use their own values not defined here.
export enum StorageKind {
unknown = 'unknown',
rdb = 'relational',
doc = 'document',
graph = 'graph',
keyval = 'key-value',
widecol = 'wide-column',
}
StoragePool
interface StoragePool extends StorageApi {
conn(ctx: IContext): Promise<StorageConn>;
beginTxn(ctx: IContext, opts?: TxnOptions): Promise<StorageTxn>;
close(): Promise<void>;
}
A pool of storage connections.
Implementations of StoragePool
should return StorageMode.pool
for their mode
property.
conn
Retrieves a connection from the pool. The context provided may be cancelable, and if the context is canceled before a connection becomes available then conn
should throw an exception. The resolved connection should already be open and ready for use.
If ctx is canceled: If a connection has already been returned, nothing happens. The cancellation applies only to the request to obtain a connection.
beginTxn
Begins a transaction on a transient connection that will be returned to the pool when the transaction completes. Implementers should respect a cancelable context and rollback the transaction if the context is canceled before the transaction is committed.
If ctx is canceled: Any ongoing operations on the transaction returned from beginTxn
are immediately aborted, the transaction is rolled back, and the underlying connection is closed and returned to the pool.
close
Closes the entire pool. Pools are meant to be long-lived and concurrent-safe, so this is generally only used on graceful program termination. Should resolve when all connections have been gracefully terminated.
StorageConn
export interface StorageConn extends StorageApi {
beginTxn(ctx: IContext, opts?: unknown): Promise<StorageTxn>;
close(): Promise<void>;
}
An open connection to a storage provider. Maintains session state such as variables, temporary tables, and transactions. Users of a connection are expected to ensure the connection is closed when they are done with it.
Implementations of StorageConn
should return StorageMode.conn
for their mode
property.
beginTxn
Begins a transaction on the connection. Implementers should respect a cancelable context and rollback the transaction if the context is canceled before the transaction is committed.
If ctx is canceled: Any ongoing operations on the transaction returned from beginTxn
are immediately aborted and the transaction is rolled back, but the connection itself remains open.
close
Closes the connection, waiting for all ongoing operations and transactions to complete. If the connection was obtained from a pool, this should release the connection back to the pool rather than terminating the underlying connection.
StorageTxn
interface StorageTxn extends StorageApi {
commit(): Promise<void>;
rollback(): Promise<void>;
}
An active storage transaction.
Implementations of StorageTxn
should return StorageMode.txn
for their mode
property.
commit
Commits and closes the transaction.
rollback
Rolls back and closes the transaction.
Concepts
Many storage clients are able to pool connections to a remote data store. Consuming code should retrieve a connection from the pool when it needs one, and promptly return the connection when the work is done, whether or not the work was successful.
These concepts are represented by the StoragePool
and StorageConn
interfaces.
Some storage services also support transactions. A transaction represents a series of actions whose effects either all succeed or all fail together. A transaction is represented by the StorageTxn
interface.
Type-Specific CRUD APIs
Many storage client libraries will expose the same type-specific CRUD APIs on all three basic types - pool, connection, and transaction.
For example, a document store would support APIs such as insertOne
, updateMany
, and find
:
/** Example: Common Doc store API */
interface DocStoreAPI {
insertOne(ctx: IContext, collection: string, doc:Doc, opts): Promise<void>;
insertMany(ctx: IContext, collection: string, docs: Doc[], opts): Promise<void>;
find(ctx: IContext, collection: string, filter: any): Promise<Cursor>;
... etc ...
}
All of these APIs are inherited by a DocStorePool
, DocStoreConn
, and DocStoreTxn
.
- If invoked directly on a pool, a connection is automatically acquired, used, and then released as soon as the operation is complete.
- If invoked on a connection, the connection is left open for subsequent operations
- If invoked on a transaction, the transaction is left uncommitted for subsequent operations
The actual makeup of the common storage API differs by storage type. However, this library still defines a very simple base StorageAPI
that exposes two read-only properties that allow consuming code to make basic decisions about an implementing instance without having to use fickle reflection methods such as instanceof
.
Example: StackAPI
The tests of this library include a minimal but accurate example of both the interfaces and an implementation for a type-specific api, using a simple stack as the underlying 'data store'. See source for details.
// EXAMPLE, included in test/fixtures of this repo:
// StackApi is the basic stack ops: push, peek, pop
export interface StackApi extends StorageApi {
push(ctx: IContext, val: unknown): Promise<number>;
peek(ctx: IContext): Promise<unknown>;
pop(ctx: IContext): Promise<unknown>;
}
// StackTxn is a composition of the basic StorageTxn
// (commit, rollback) with the StackApi
export interface StackTxn extends StorageTxn, StackApi {}
// Overrides basic Transactable so that the return
// value is a StackTxn
export interface StackTransactable extends Transactable {
beginTxn(ctx: IContext, opts?: TxnOptions): Promise<StackTxn>;
}
// Composition that is structurally compatible with StorageConn
export interface StackConn extends StackApi, StackTransactable {
close(): Promise<void>;
}
// Composition that is structurally compatible with StoragePool
export interface StackPool extends StackApi, StackTransactable {
conn(ctx: IContext): Promise<StackConn>;
close(): Promise<void>;
}