@launchdarkly/js-server-sdk-common
Version:
LaunchDarkly Server SDK for JavaScript - common code
118 lines • 5.12 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const js_sdk_common_1 = require("@launchdarkly/js-sdk-common");
const BigSegmentStatusProviderImpl_1 = require("./BigSegmentStatusProviderImpl");
const LruCache_1 = require("./cache/LruCache");
const DEFAULT_STALE_AFTER_SECONDS = 120;
const DEFAULT_STATUS_POLL_INTERVAL_SECONDS = 5;
const DEFAULT_USER_CACHE_SIZE = 1000;
const DEFAULT_USER_CACHE_TIME_SECONDS = 5;
class BigSegmentsManager {
constructor(_store,
// The store will have been created before the manager is instantiated, so we do not need
// it in the options at this stage.
config, _logger, _crypto) {
this._store = _store;
this._logger = _logger;
this._crypto = _crypto;
this.statusProvider = new BigSegmentStatusProviderImpl_1.default(async () => this._pollStoreAndUpdateStatus());
this._staleTimeMs =
(js_sdk_common_1.TypeValidators.Number.is(config.staleAfter) && config.staleAfter > 0
? config.staleAfter
: DEFAULT_STALE_AFTER_SECONDS) * 1000;
const pollIntervalMs = (js_sdk_common_1.TypeValidators.Number.is(config.statusPollInterval) && config.statusPollInterval > 0
? config.statusPollInterval
: DEFAULT_STATUS_POLL_INTERVAL_SECONDS) * 1000;
this._pollHandle = _store
? setInterval(() => this._pollStoreAndUpdateStatus(), pollIntervalMs)
: null;
if (_store) {
this._cache = new LruCache_1.default({
max: config.userCacheSize || DEFAULT_USER_CACHE_SIZE,
maxAge: (config.userCacheTime || DEFAULT_USER_CACHE_TIME_SECONDS) * 1000,
});
}
}
close() {
if (this._pollHandle) {
clearInterval(this._pollHandle);
this._pollHandle = undefined;
}
if (this._store) {
this._store.close();
}
}
async getUserMembership(userKey) {
var _a, _b, _c;
if (!this._store) {
return undefined;
}
const memberCache = (_a = this._cache) === null || _a === void 0 ? void 0 : _a.get(userKey);
let membership;
if (!memberCache) {
try {
membership = await this._store.getUserMembership(this._hashForUserKey(userKey));
const cacheItem = { membership };
(_b = this._cache) === null || _b === void 0 ? void 0 : _b.set(userKey, cacheItem);
}
catch (err) {
(_c = this._logger) === null || _c === void 0 ? void 0 : _c.error(`Big Segment store membership query returned error: ${err}`);
return [null, 'STORE_ERROR'];
}
}
else {
membership = memberCache.membership;
}
if (!this.statusProvider.getStatus()) {
await this._pollStoreAndUpdateStatus();
}
// Status will be present, because polling is done earlier in this method if it is not.
const lastStatus = this.statusProvider.getStatus();
if (!lastStatus.available) {
return [membership || null, 'STORE_ERROR'];
}
return [membership || null, lastStatus.stale ? 'STALE' : 'HEALTHY'];
}
async _pollStoreAndUpdateStatus() {
var _a, _b, _c;
if (!this._store) {
this.statusProvider.setStatus({ available: false, stale: false });
return;
}
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug('Querying Big Segment store status');
let newStatus;
try {
const metadata = await this._store.getMetadata();
newStatus = {
available: true,
stale: !metadata || !metadata.lastUpToDate || this._isStale(metadata.lastUpToDate),
};
}
catch (err) {
(_b = this._logger) === null || _b === void 0 ? void 0 : _b.error(`Big Segment store status query returned error: ${err}`);
newStatus = { available: false, stale: false };
}
const lastStatus = this.statusProvider.getStatus();
if (!lastStatus ||
lastStatus.available !== newStatus.available ||
lastStatus.stale !== newStatus.stale) {
(_c = this._logger) === null || _c === void 0 ? void 0 : _c.debug('Big Segment store status changed from %s to %s', JSON.stringify(lastStatus), JSON.stringify(newStatus));
this.statusProvider.setStatus(newStatus);
this.statusProvider.notify();
}
}
_hashForUserKey(userKey) {
const hasher = this._crypto.createHash('sha256');
hasher.update(userKey);
if (!hasher.digest) {
// This represents an error in platform implementation.
throw new Error('Platform must implement digest or asyncDigest');
}
return hasher.digest('base64');
}
_isStale(timestamp) {
return Date.now() - timestamp >= this._staleTimeMs;
}
}
exports.default = BigSegmentsManager;
//# sourceMappingURL=BigSegmentsManager.js.map