unleash-server
Version:
Unleash is an enterprise ready feature flag service. It provides different strategies for handling feature flags.
104 lines • 4.03 kB
JavaScript
import HyperLogLog from 'hyperloglog-lite';
import { SDK_CONNECTION_ID_RECEIVED } from '../../metric-events.js';
import { REGISTERS_EXPONENT } from './hyperloglog-config.js';
export class UniqueConnectionService {
constructor({ uniqueConnectionStore, }, config) {
this.hll = HyperLogLog(REGISTERS_EXPONENT);
this.backendHll = HyperLogLog(REGISTERS_EXPONENT);
this.frontendHll = HyperLogLog(REGISTERS_EXPONENT);
this.uniqueConnectionStore = uniqueConnectionStore;
this.logger = config.getLogger('services/unique-connection-service.ts');
this.flagResolver = config.flagResolver;
this.eventBus = config.eventBus;
this.activeHour = new Date().getHours();
}
listen() {
this.eventBus.on(SDK_CONNECTION_ID_RECEIVED, this.count.bind(this));
}
count({ connectionId, type, }) {
if (!this.flagResolver.isEnabled('uniqueSdkTracking'))
return;
const value = HyperLogLog.hash(connectionId);
this.hll.add(value);
if (type === 'frontend') {
this.frontendHll.add(value);
}
else if (type === 'backend') {
this.backendHll.add(value);
}
}
async sync(currentTime = new Date()) {
if (!this.flagResolver.isEnabled('uniqueSdkTracking'))
return;
const currentHour = currentTime.getHours();
await this.syncBuckets(currentTime, 'current', 'previous');
await this.syncBuckets(currentTime, 'currentBackend', 'previousBackend');
await this.syncBuckets(currentTime, 'currentFrontend', 'previousFrontend');
if (this.activeHour !== currentHour) {
this.activeHour = currentHour;
}
}
resetHll(bucketId) {
if (bucketId.toLowerCase().includes('frontend')) {
this.frontendHll = HyperLogLog(REGISTERS_EXPONENT);
}
else if (bucketId.toLowerCase().includes('backend')) {
this.backendHll = HyperLogLog(REGISTERS_EXPONENT);
}
else {
this.hll = HyperLogLog(REGISTERS_EXPONENT);
}
}
getHll(bucketId) {
if (bucketId.toLowerCase().includes('frontend')) {
return this.frontendHll;
}
else if (bucketId.toLowerCase().includes('backend')) {
return this.backendHll;
}
else {
return this.hll;
}
}
async syncBuckets(currentTime, current, previous) {
const currentHour = currentTime.getHours();
const currentBucket = await this.uniqueConnectionStore.get(current);
if (this.activeHour !== currentHour && currentBucket) {
if (currentBucket.updatedAt.getHours() < currentHour) {
this.getHll(current).merge({
n: REGISTERS_EXPONENT,
buckets: currentBucket.hll,
});
await this.uniqueConnectionStore.insert({
hll: this.getHll(current).output().buckets,
id: previous,
});
}
else {
const previousBucket = await this.uniqueConnectionStore.get(previous);
if (previousBucket) {
this.getHll(current).merge({
n: REGISTERS_EXPONENT,
buckets: previousBucket.hll,
});
}
await this.uniqueConnectionStore.insert({
hll: this.getHll(current).output().buckets,
id: previous,
});
}
this.resetHll(current);
}
else if (currentBucket) {
this.getHll(current).merge({
n: REGISTERS_EXPONENT,
buckets: currentBucket.hll,
});
}
await this.uniqueConnectionStore.insert({
hll: this.getHll(current).output().buckets,
id: current,
});
}
}
//# sourceMappingURL=unique-connection-service.js.map