UNPKG

@launchdarkly/js-server-sdk-common

Version:
216 lines 8.95 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const TtlCache_1 = require("../cache/TtlCache"); const persistentStoreKinds_1 = require("./persistentStoreKinds"); const sortDataSet_1 = require("./sortDataSet"); const UpdateQueue_1 = require("./UpdateQueue"); function cacheKey(kind, key) { return `${kind.namespace}:${key}`; } function allForKindCacheKey(kind) { return `$all:${kind.namespace}`; } // This key will be set in the cache if we have checked that a store was // initialized and found that it was not. If we do not become initialized // within the TTL period, then the key will expire, and the next initialization // check will pass through to the store. Once we are initialized, then the key // will never be checked again. const initializationCheckedKey = '$checkedInit'; // The interval to check items in the TTL cache to purge expired items. // Expired items which are not purged, will be reactively purged when they are // accessed. const defaultCheckInterval = 600; function itemIfNotDeleted(item) { return !item || item.item.deleted ? null : item.item; } function deletedDescriptor(version) { return { version, item: { version, deleted: true }, }; } /** * Deserialize a {@link SerializedItemDescriptor} * @param kind The persistent store data kind to deserialize. * @param descriptor The serialized descriptor we want to deserialize. * @returns An item descriptor for the deserialized item. */ function deserialize(kind, descriptor) { if (descriptor.deleted || !descriptor.serializedItem) { return deletedDescriptor(descriptor.version); } const deserializedItem = kind.deserialize(descriptor.serializedItem); if (deserializedItem === undefined) { // This would only happen if the JSON is invalid. return deletedDescriptor(descriptor.version); } if (deserializedItem.version === 0 || deserializedItem.version === descriptor.version || deserializedItem.item === undefined) { return deserializedItem; } // There was a mismatch between the version of the serialized descriptor and the deserialized // descriptor. So we are going to trust the version of the serialized descriptor. return { version: descriptor.version, item: deserializedItem.item, }; } /** * Internal implementation of {@link LDFeatureStore} that delegates the basic functionality to an * instance of {@link PersistentDataStore}. It provides optional caching behavior and other logic * that would otherwise be repeated in every data store implementation. This makes it easier to * create new database integrations by implementing only the database-specific logic. */ class PersistentDataStoreWrapper { constructor(_core, ttl) { this._core = _core; this._isInitialized = false; /** * Used to preserve order of operations of async requests. */ this._queue = new UpdateQueue_1.default(); if (ttl) { this._itemCache = new TtlCache_1.default({ ttl, checkInterval: defaultCheckInterval, }); this._allItemsCache = new TtlCache_1.default({ ttl, checkInterval: defaultCheckInterval, }); } } init(allData, callback) { this._queue.enqueue((cb) => { const afterStoreInit = () => { this._isInitialized = true; if (this._itemCache) { this._itemCache.clear(); this._allItemsCache.clear(); Object.keys(allData).forEach((kindNamespace) => { const kind = persistentStoreKinds_1.persistentStoreKinds[kindNamespace]; const items = allData[kindNamespace]; this._allItemsCache.set(allForKindCacheKey(kind), items); Object.keys(items).forEach((key) => { const itemForKey = items[key]; const itemDescriptor = { version: itemForKey.version, item: itemForKey, }; this._itemCache.set(cacheKey(kind, key), itemDescriptor); }); }); } cb(); }; this._core.init((0, sortDataSet_1.default)(allData), afterStoreInit); }, callback); } get(kind, key, callback) { if (this._itemCache) { const item = this._itemCache.get(cacheKey(kind, key)); if (item) { callback(itemIfNotDeleted(item)); return; } } const persistKind = persistentStoreKinds_1.persistentStoreKinds[kind.namespace]; this._core.get(persistKind, key, (descriptor) => { var _a; if (descriptor && descriptor.serializedItem) { const value = deserialize(persistKind, descriptor); (_a = this._itemCache) === null || _a === void 0 ? void 0 : _a.set(cacheKey(kind, key), value); callback(itemIfNotDeleted(value)); return; } callback(null); }); } initialized(callback) { var _a; if (this._isInitialized) { callback(true); } else if ((_a = this._itemCache) === null || _a === void 0 ? void 0 : _a.get(initializationCheckedKey)) { callback(false); } else { this._core.initialized((storeInitialized) => { var _a; this._isInitialized = storeInitialized; if (!this._isInitialized) { (_a = this._itemCache) === null || _a === void 0 ? void 0 : _a.set(initializationCheckedKey, true); } callback(this._isInitialized); }); } } all(kind, callback) { var _a; const items = (_a = this._allItemsCache) === null || _a === void 0 ? void 0 : _a.get(allForKindCacheKey(kind)); if (items) { callback(items); return; } const persistKind = persistentStoreKinds_1.persistentStoreKinds[kind.namespace]; this._core.getAll(persistKind, (storeItems) => { var _a; if (!storeItems) { callback({}); return; } const filteredItems = {}; storeItems.forEach(({ key, item }) => { const deserializedItem = deserialize(persistKind, item); const filteredItem = itemIfNotDeleted(deserializedItem); if (filteredItem) { filteredItems[key] = filteredItem; } }); (_a = this._allItemsCache) === null || _a === void 0 ? void 0 : _a.set(allForKindCacheKey(kind), filteredItems); callback(filteredItems); }); } upsert(kind, data, callback) { this._queue.enqueue((cb) => { // Clear the caches which contain all the values of a specific kind. if (this._allItemsCache) { this._allItemsCache.clear(); } const persistKind = persistentStoreKinds_1.persistentStoreKinds[kind.namespace]; this._core.upsert(persistKind, data.key, persistKind.serialize(data), (err, updatedDescriptor) => { var _a, _b; if (!err && updatedDescriptor) { if (updatedDescriptor.serializedItem) { const value = deserialize(persistKind, updatedDescriptor); (_a = this._itemCache) === null || _a === void 0 ? void 0 : _a.set(cacheKey(kind, data.key), value); } else if (updatedDescriptor.deleted) { // Deleted and there was not a serialized representation. (_b = this._itemCache) === null || _b === void 0 ? void 0 : _b.set(data.key, { key: data.key, version: updatedDescriptor.version, deleted: true, }); } } cb(); }); }, callback); } delete(kind, key, version, callback) { this.upsert(kind, { key, version, deleted: true }, callback); } close() { var _a, _b; (_a = this._itemCache) === null || _a === void 0 ? void 0 : _a.close(); (_b = this._allItemsCache) === null || _b === void 0 ? void 0 : _b.close(); this._core.close(); } getDescription() { return this._core.getDescription(); } } exports.default = PersistentDataStoreWrapper; //# sourceMappingURL=PersistentDataStoreWrapper.js.map