couchbase
Version:
The official Couchbase Node.js Client Library.
407 lines (406 loc) • 13.8 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Transactions = exports.TransactionAttemptContext = exports.TransactionQueryResult = exports.TransactionGetResult = exports.TransactionResult = exports.DocumentId = void 0;
const binding_1 = __importDefault(require("./binding"));
const bindingutilities_1 = require("./bindingutilities");
const errors_1 = require("./errors");
const queryexecutor_1 = require("./queryexecutor");
const transcoders_1 = require("./transcoders");
const utilities_1 = require("./utilities");
/**
* Represents the path to a document.
*
* @category Transactions
*/
class DocumentId {
constructor() {
this.bucket = '';
this.scope = '';
this.collection = '';
this.key = '';
}
}
exports.DocumentId = DocumentId;
/**
* Contains the results of a Transaction.
*
* @category Transactions
*/
class TransactionResult {
/**
* @internal
*/
constructor(data) {
this.transactionId = data.transactionId;
this.unstagingComplete = data.unstagingComplete;
}
}
exports.TransactionResult = TransactionResult;
/**
* Contains the results of a transactional Get operation.
*
* @category Transactions
*/
class TransactionGetResult {
/**
* @internal
*/
constructor(data) {
this.id = data.id;
this.content = data.content;
this.cas = data.cas;
this._links = data._links;
this._metadata = data._metadata;
}
}
exports.TransactionGetResult = TransactionGetResult;
/**
* Contains the results of a transactional Query operation.
*
* @category Transactions
*/
class TransactionQueryResult {
/**
* @internal
*/
constructor(data) {
this.rows = data.rows;
this.meta = data.meta;
}
}
exports.TransactionQueryResult = TransactionQueryResult;
/**
* @internal
*/
function translateGetResult(cppRes, transcoder) {
if (!cppRes) {
return null;
}
let content;
if (cppRes.content && cppRes.content.data && cppRes.content.data.length > 0) {
content = transcoder.decode(cppRes.content.data, cppRes.content.flags);
}
return new TransactionGetResult({
id: cppRes.id,
content: content,
cas: cppRes.cas,
_links: cppRes.links,
_metadata: cppRes.metadata,
});
}
/**
* Provides an interface to preform transactional operations in a transaction.
*
* @category Transactions
*/
class TransactionAttemptContext {
/**
* @internal
*/
constructor(txns, config) {
if (!config) {
config = {};
}
this._impl = new binding_1.default.Transaction(txns.impl, {
durability_level: (0, bindingutilities_1.durabilityToCpp)(config.durabilityLevel),
timeout: config.timeout,
query_scan_consistency: (0, bindingutilities_1.queryScanConsistencyToCpp)(undefined),
});
this._transcoder = new transcoders_1.DefaultTranscoder();
}
/**
@internal
*/
get impl() {
return this._impl;
}
/**
* @internal
*/
_newAttempt() {
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
this._impl.newAttempt((cppErr) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
wrapCallback(err);
});
});
}
/**
* Retrieves the value of a document from the collection.
*
* @param collection The collection the document lives in.
* @param key The document key to retrieve.
* @param options Optional parameters for this operation.
*/
async get(collection, key, options) {
const transcoder = (options === null || options === void 0 ? void 0 : options.transcoder) || this._transcoder;
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
const id = collection._cppDocId(key);
this._impl.get({
id,
}, (cppErr, cppRes) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
return wrapCallback(err, null);
}
wrapCallback(err, translateGetResult(cppRes, transcoder));
});
});
}
/**
* Inserts a new document to the collection, failing if the document already exists.
*
* @param collection The collection the document lives in.
* @param key The document key to insert.
* @param content The document content to insert.
* @param options Optional parameters for this operation.
*/
async insert(collection, key, content, options) {
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
const id = collection._cppDocId(key);
const transcoder = (options === null || options === void 0 ? void 0 : options.transcoder) || this._transcoder;
const [data, flags] = transcoder.encode(content);
this._impl.insert({
id,
content: {
data,
flags,
},
}, (cppErr, cppRes) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
return wrapCallback(err, null);
}
wrapCallback(err, translateGetResult(cppRes, transcoder));
});
});
}
/**
* Replaces a document in a collection.
*
* @param doc The document to replace.
* @param content The document content to insert.
* @param options Optional parameters for this operation.
*/
async replace(doc, content, options) {
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
const transcoder = (options === null || options === void 0 ? void 0 : options.transcoder) || this._transcoder;
const [data, flags] = transcoder.encode(content);
this._impl.replace({
doc: {
id: doc.id,
content: {
data: Buffer.from(''),
flags: 0,
},
cas: doc.cas,
links: doc._links,
metadata: doc._metadata,
},
content: {
data,
flags,
},
}, (cppErr, cppRes) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
return wrapCallback(err, null);
}
wrapCallback(err, translateGetResult(cppRes, transcoder));
});
});
}
/**
* Removes a document from a collection.
*
* @param doc The document to remove.
*/
async remove(doc) {
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
this._impl.remove({
doc: {
id: doc.id,
content: {
data: Buffer.from(''),
flags: 0,
},
cas: doc.cas,
links: doc._links,
metadata: doc._metadata,
},
}, (cppErr) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
wrapCallback(err, null);
});
});
}
/**
* Executes a query in the context of this transaction.
*
* @param statement The statement to execute.
* @param options Optional parameters for this operation.
*/
async query(statement, options) {
// This await statement is explicit here to ensure our query is completely
// processed before returning the result to the user (no row streaming).
const syncQueryRes = await queryexecutor_1.QueryExecutor.execute((callback) => {
if (!options) {
options = {};
}
this._impl.query(statement, {
scan_consistency: (0, bindingutilities_1.queryScanConsistencyToCpp)(options.scanConsistency),
ad_hoc: options.adhoc === false ? false : true,
client_context_id: options.clientContextId,
pipeline_batch: options.pipelineBatch,
pipeline_cap: options.pipelineCap,
max_parallelism: options.maxParallelism,
scan_wait: options.scanWait,
scan_cap: options.scanCap,
readonly: options.readOnly || false,
profile: (0, bindingutilities_1.queryProfileToCpp)(options.profile),
metrics: options.metrics || false,
raw: options.raw
? Object.fromEntries(Object.entries(options.raw)
.filter(([, v]) => v !== undefined)
.map(([k, v]) => [k, Buffer.from(JSON.stringify(v))]))
: {},
positional_parameters: options.parameters && Array.isArray(options.parameters)
? options.parameters.map((v) => Buffer.from(JSON.stringify(v !== null && v !== void 0 ? v : null)))
: [],
named_parameters: options.parameters && !Array.isArray(options.parameters)
? Object.fromEntries(Object.entries(options.parameters)
.filter(([, v]) => v !== undefined)
.map(([k, v]) => [k, Buffer.from(JSON.stringify(v))]))
: {},
}, (cppErr, resp) => {
callback(cppErr, resp);
});
});
return new TransactionQueryResult({
rows: syncQueryRes.rows,
meta: syncQueryRes.meta,
});
}
/**
* @internal
*/
async _commit() {
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
this._impl.commit((cppErr, cppRes) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
let res = null;
if (cppRes) {
res = new TransactionResult({
transactionId: cppRes.transaction_id,
unstagingComplete: cppRes.unstaging_complete,
});
}
wrapCallback(err, res);
});
});
}
/**
* @internal
*/
async _rollback() {
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
this._impl.rollback((cppErr) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
wrapCallback(err);
});
});
}
}
exports.TransactionAttemptContext = TransactionAttemptContext;
/**
* Provides an interface to access transactions.
*
* @category Transactions
*/
class Transactions {
/**
@internal
*/
constructor(cluster, config) {
if (!config) {
config = {};
}
if (!config.cleanupConfig) {
config.cleanupConfig = {};
}
if (!config.queryConfig) {
config.queryConfig = {};
}
const connImpl = cluster.conn;
try {
const txnsImpl = new binding_1.default.Transactions(connImpl, {
durability_level: (0, bindingutilities_1.durabilityToCpp)(config.durabilityLevel),
timeout: config.timeout,
query_scan_consistency: (0, bindingutilities_1.queryScanConsistencyToCpp)(config.queryConfig.scanConsistency),
cleanup_window: config.cleanupConfig.cleanupWindow,
cleanup_lost_attempts: !config.cleanupConfig.disableLostAttemptCleanup,
cleanup_client_attempts: !config.cleanupConfig.disableClientAttemptCleanup,
metadata_collection: (0, bindingutilities_1.transactionKeyspaceToCpp)(config.metadataCollection),
});
this._cluster = cluster;
this._impl = txnsImpl;
}
catch (err) {
throw (0, bindingutilities_1.errorFromCpp)(err);
}
}
/**
@internal
*/
get impl() {
return this._impl;
}
/**
@internal
*/
_close() {
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
this._impl.close((cppErr) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
wrapCallback(err, null);
});
});
}
/**
* Executes a transaction.
*
* @param logicFn The transaction lambda to execute.
* @param config Configuration operations for the transaction.
*/
async run(logicFn, config) {
const txn = new TransactionAttemptContext(this, config);
for (;;) {
await txn._newAttempt();
try {
await logicFn(txn);
}
catch (e) {
await txn._rollback();
if (e instanceof errors_1.TransactionOperationFailedError) {
throw new errors_1.TransactionFailedError(e.cause, e.context);
}
throw new errors_1.TransactionFailedError(e);
}
try {
const txnResult = await txn._commit(); // this is actually finalize internally
if (!txnResult) {
// no result and no error, try again
continue;
}
return txnResult;
}
catch (e) {
// commit failed, retry...
}
}
}
}
exports.Transactions = Transactions;
;