1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-07-31 13:47:02 +02:00

chore: rebrand cache to delta (#8986)

The cache is too generic, we will be using delta from now on.
This commit is contained in:
Jaanus Sellin 2024-12-16 12:51:03 +02:00 committed by GitHub
parent a2f82716d1
commit 3696420c51
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 89 additions and 89 deletions

View File

@ -10,9 +10,9 @@ import type { Logger } from '../../logger';
import type { FeatureConfigurationClient } from '../feature-toggle/types/feature-toggle-strategies-store-type';
import type {
RevisionCacheEntry,
ClientFeatureToggleCache,
} from './cache/client-feature-toggle-cache';
RevisionDeltaEntry,
ClientFeatureToggleDelta,
} from './delta/client-feature-toggle-delta';
export class ClientFeatureToggleService {
private logger: Logger;
@ -21,19 +21,19 @@ export class ClientFeatureToggleService {
private segmentReadModel: ISegmentReadModel;
private clientFeatureToggleCache: ClientFeatureToggleCache | null = null;
private clientFeatureToggleDelta: ClientFeatureToggleDelta | null = null;
constructor(
{
clientFeatureToggleStore,
}: Pick<IUnleashStores, 'clientFeatureToggleStore'>,
segmentReadModel: ISegmentReadModel,
clientFeatureToggleCache: ClientFeatureToggleCache | null,
clientFeatureToggleCache: ClientFeatureToggleDelta | null,
{ getLogger }: Pick<IUnleashConfig, 'getLogger' | 'flagResolver'>,
) {
this.logger = getLogger('services/client-feature-toggle-service.ts');
this.segmentReadModel = segmentReadModel;
this.clientFeatureToggleCache = clientFeatureToggleCache;
this.clientFeatureToggleDelta = clientFeatureToggleCache;
this.clientFeatureToggleStore = clientFeatureToggleStore;
}
@ -44,9 +44,9 @@ export class ClientFeatureToggleService {
async getClientDelta(
revisionId: number | undefined,
query: IFeatureToggleQuery,
): Promise<RevisionCacheEntry | undefined> {
if (this.clientFeatureToggleCache !== null) {
return this.clientFeatureToggleCache.getDelta(revisionId, query);
): Promise<RevisionDeltaEntry | undefined> {
if (this.clientFeatureToggleDelta !== null) {
return this.clientFeatureToggleDelta.getDelta(revisionId, query);
} else {
throw new Error(
'Calling the partial updates but the cache is not initialized',

View File

@ -5,7 +5,7 @@ import FakeClientFeatureToggleStore from './fakes/fake-client-feature-toggle-sto
import { ClientFeatureToggleService } from './client-feature-toggle-service';
import { SegmentReadModel } from '../segment/segment-read-model';
import { FakeSegmentReadModel } from '../segment/fake-segment-read-model';
import { createClientFeatureToggleCache } from './cache/createClientFeatureToggleCache';
import { createClientFeatureToggleDelta } from './delta/createClientFeatureToggleDelta';
export const createClientFeatureToggleService = (
db: Db,
@ -22,7 +22,7 @@ export const createClientFeatureToggleService = (
const segmentReadModel = new SegmentReadModel(db);
const clientFeatureToggleCache = createClientFeatureToggleCache(db, config);
const clientFeatureToggleCache = createClientFeatureToggleDelta(db, config);
const clientFeatureToggleService = new ClientFeatureToggleService(
{

View File

@ -17,7 +17,7 @@ import type { OpenApiService } from '../../../services/openapi-service';
import { NONE } from '../../../types/permissions';
import { createResponseSchema } from '../../../openapi/util/create-response-schema';
import type { ClientFeatureToggleService } from '../client-feature-toggle-service';
import type { RevisionCacheEntry } from './client-feature-toggle-cache';
import type { RevisionDeltaEntry } from './client-feature-toggle-delta';
import { clientFeaturesDeltaSchema } from '../../../openapi';
import type { QueryOverride } from '../client-feature-toggle.controller';
@ -142,7 +142,7 @@ export default class ClientFeatureToggleDeltaController extends Controller {
async getDelta(
req: IAuthRequest,
res: Response<RevisionCacheEntry>,
res: Response<RevisionDeltaEntry>,
): Promise<void> {
if (!this.flagResolver.isEnabled('deltaApi')) {
throw new NotFoundError();

View File

@ -1,14 +1,14 @@
import type { IFeatureToggleQuery } from '../../../types';
import type { FeatureConfigurationClient } from '../../feature-toggle/types/feature-toggle-strategies-store-type';
export interface FeatureConfigurationCacheClient
export interface FeatureConfigurationDeltaClient
extends FeatureConfigurationClient {
description: string;
impressionData: false;
}
export interface IClientFeatureToggleCacheReadModel {
export interface IClientFeatureToggleDeltaReadModel {
getAll(
featureQuery: IFeatureToggleQuery,
): Promise<FeatureConfigurationCacheClient[]>;
): Promise<FeatureConfigurationDeltaClient[]>;
}

View File

@ -5,21 +5,21 @@ import Raw = Knex.Raw;
import type EventEmitter from 'events';
import { ALL_PROJECTS, ensureStringValue, mapValues } from '../../../util';
import type {
FeatureConfigurationCacheClient,
IClientFeatureToggleCacheReadModel,
} from './client-feature-toggle-cache-read-model-type';
FeatureConfigurationDeltaClient,
IClientFeatureToggleDeltaReadModel,
} from './client-feature-toggle-delta-read-model-type';
import type { Db } from '../../../db/db';
import {
DB_TIME,
type IFeatureToggleCacheQuery,
type IFeatureToggleDeltaQuery,
type IStrategyConfig,
type PartialDeep,
} from '../../../internals';
import metricsHelper from '../../../util/metrics-helper';
import FeatureToggleStore from '../../feature-toggle/feature-toggle-store';
export default class ClientFeatureToggleCacheReadModel
implements IClientFeatureToggleCacheReadModel
export default class ClientFeatureToggleDeltaReadModel
implements IClientFeatureToggleDeltaReadModel
{
private db: Db;
@ -29,14 +29,14 @@ export default class ClientFeatureToggleCacheReadModel
this.db = db;
this.timer = (action: string) =>
metricsHelper.wrapTimer(eventBus, DB_TIME, {
store: 'client-feature-toggle-cache-read-model',
store: 'client-feature-toggle-delta-read-model',
action,
});
}
public async getAll(
featureQuery: IFeatureToggleCacheQuery,
): Promise<FeatureConfigurationCacheClient[]> {
featureQuery: IFeatureToggleDeltaQuery,
): Promise<FeatureConfigurationDeltaClient[]> {
const environment = featureQuery.environment;
const stopTimer = this.timer(`getAll`);
@ -128,7 +128,7 @@ export default class ClientFeatureToggleCacheReadModel
stopTimer();
const featureToggles = rows.reduce((acc, r) => {
const feature: PartialDeep<FeatureConfigurationCacheClient> = acc[
const feature: PartialDeep<FeatureConfigurationDeltaClient> = acc[
r.name
] ?? {
strategies: [],
@ -168,7 +168,7 @@ export default class ClientFeatureToggleCacheReadModel
return acc;
}, {});
const features: FeatureConfigurationCacheClient[] =
const features: FeatureConfigurationDeltaClient[] =
Object.values(featureToggles);
// strip away unwanted properties
@ -193,7 +193,7 @@ export default class ClientFeatureToggleCacheReadModel
}
private addSegmentIdsToStrategy(
feature: PartialDeep<FeatureConfigurationCacheClient>,
feature: PartialDeep<FeatureConfigurationDeltaClient>,
row: Record<string, any>,
) {
const strategy = feature.strategies?.find(
@ -222,7 +222,7 @@ export default class ClientFeatureToggleCacheReadModel
}
private isUnseenStrategyRow(
feature: PartialDeep<FeatureConfigurationCacheClient>,
feature: PartialDeep<FeatureConfigurationDeltaClient>,
row: Record<string, any>,
): boolean {
return (
@ -232,7 +232,7 @@ export default class ClientFeatureToggleCacheReadModel
}
private addSegmentToStrategy(
feature: PartialDeep<FeatureConfigurationCacheClient>,
feature: PartialDeep<FeatureConfigurationDeltaClient>,
row: Record<string, any>,
) {
feature.strategies

View File

@ -1,4 +1,4 @@
import { calculateRequiredClientRevision } from './client-feature-toggle-cache';
import { calculateRequiredClientRevision } from './client-feature-toggle-delta';
const mockAdd = (params): any => {
const base = {

View File

@ -1,24 +1,24 @@
import type {
IEventStore,
IFeatureToggleCacheQuery,
IFeatureToggleDeltaQuery,
IFeatureToggleQuery,
IFlagResolver,
} from '../../../types';
import type ConfigurationRevisionService from '../../feature-toggle/configuration-revision-service';
import { UPDATE_REVISION } from '../../feature-toggle/configuration-revision-service';
import { RevisionCache } from './revision-cache';
import { RevisionDelta } from './revision-delta';
import type {
FeatureConfigurationCacheClient,
IClientFeatureToggleCacheReadModel,
} from './client-feature-toggle-cache-read-model-type';
FeatureConfigurationDeltaClient,
IClientFeatureToggleDeltaReadModel,
} from './client-feature-toggle-delta-read-model-type';
type DeletedFeature = {
name: string;
project: string;
};
export type RevisionCacheEntry = {
updated: FeatureConfigurationCacheClient[];
export type RevisionDeltaEntry = {
updated: FeatureConfigurationDeltaClient[];
revisionId: number;
removed: DeletedFeature[];
};
@ -29,7 +29,7 @@ export type Revision = {
removed: DeletedFeature[];
};
type Revisions = Record<string, RevisionCache>;
type Revisions = Record<string, RevisionDelta>;
const applyRevision = (first: Revision, last: Revision): Revision => {
const updatedMap = new Map(
@ -91,10 +91,10 @@ export const calculateRequiredClientRevision = (
return projectFeatureRevisions.reduce(applyRevision);
};
export class ClientFeatureToggleCache {
private clientFeatureToggleCacheReadModel: IClientFeatureToggleCacheReadModel;
export class ClientFeatureToggleDelta {
private clientFeatureToggleDeltaReadModel: IClientFeatureToggleDeltaReadModel;
private cache: Revisions = {};
private delta: Revisions = {};
private eventStore: IEventStore;
@ -107,18 +107,18 @@ export class ClientFeatureToggleCache {
private configurationRevisionService: ConfigurationRevisionService;
constructor(
clientFeatureToggleCacheReadModel: IClientFeatureToggleCacheReadModel,
clientFeatureToggleDeltaReadModel: IClientFeatureToggleDeltaReadModel,
eventStore: IEventStore,
configurationRevisionService: ConfigurationRevisionService,
flagResolver: IFlagResolver,
) {
this.eventStore = eventStore;
this.configurationRevisionService = configurationRevisionService;
this.clientFeatureToggleCacheReadModel =
clientFeatureToggleCacheReadModel;
this.clientFeatureToggleDeltaReadModel =
clientFeatureToggleDeltaReadModel;
this.flagResolver = flagResolver;
this.onUpdateRevisionEvent = this.onUpdateRevisionEvent.bind(this);
this.cache = {};
this.delta = {};
this.initRevisionId();
this.configurationRevisionService.on(
@ -135,29 +135,29 @@ export class ClientFeatureToggleCache {
async getDelta(
sdkRevisionId: number | undefined,
query: IFeatureToggleQuery,
): Promise<RevisionCacheEntry | undefined> {
): Promise<RevisionDeltaEntry | undefined> {
const projects = query.project ? query.project : ['*'];
const environment = query.environment ? query.environment : 'default';
// TODO: filter by tags, what is namePrefix? anything else?
const requiredRevisionId = sdkRevisionId || 0;
const hasCache = this.cache[environment] !== undefined;
const hasDelta = this.delta[environment] !== undefined;
if (!hasCache) {
await this.initEnvironmentCache(environment);
if (!hasDelta) {
await this.initEnvironmentDelta(environment);
}
// Should get the latest state if revision does not exist or if sdkRevision is not present
// We should be able to do this without going to the database by merging revisions from the cache with
// We should be able to do this without going to the database by merging revisions from the delta with
// the base case
const firstTimeCalling = !sdkRevisionId;
if (
firstTimeCalling ||
(sdkRevisionId &&
sdkRevisionId !== this.currentRevisionId &&
!this.cache[environment].hasRevision(sdkRevisionId))
!this.delta[environment].hasRevision(sdkRevisionId))
) {
//TODO: populate cache based on this?
//TODO: populate delta based on this?
return {
revisionId: this.currentRevisionId,
// @ts-ignore
@ -170,7 +170,7 @@ export class ClientFeatureToggleCache {
return undefined;
}
const environmentRevisions = this.cache[environment].getRevisions();
const environmentRevisions = this.delta[environment].getRevisions();
const compressedRevision = calculateRequiredClientRevision(
environmentRevisions,
@ -188,7 +188,7 @@ export class ClientFeatureToggleCache {
}
public async listenToRevisionChange() {
const keys = Object.keys(this.cache);
const keys = Object.keys(this.delta);
if (keys.length === 0) return;
const latestRevision =
@ -221,7 +221,7 @@ export class ClientFeatureToggleCache {
environment,
changedToggles,
);
this.cache[environment].addRevision({
this.delta[environment].addRevision({
updated: newToggles,
revisionId: latestRevision,
removed,
@ -234,7 +234,7 @@ export class ClientFeatureToggleCache {
async getChangedToggles(
environment: string,
toggles: string[],
): Promise<FeatureConfigurationCacheClient[]> {
): Promise<FeatureConfigurationDeltaClient[]> {
const foundToggles = await this.getClientFeatures({
toggleNames: toggles,
environment,
@ -242,7 +242,7 @@ export class ClientFeatureToggleCache {
return foundToggles;
}
public async initEnvironmentCache(environment: string) {
public async initEnvironmentDelta(environment: string) {
// Todo: replace with method that gets all features for an environment
const baseFeatures = await this.getClientFeatures({
environment,
@ -251,7 +251,7 @@ export class ClientFeatureToggleCache {
this.currentRevisionId =
await this.configurationRevisionService.getMaxRevisionId();
const cache = new RevisionCache([
const delta = new RevisionDelta([
{
revisionId: this.currentRevisionId,
updated: baseFeatures,
@ -259,14 +259,14 @@ export class ClientFeatureToggleCache {
},
]);
this.cache[environment] = cache;
this.delta[environment] = delta;
}
async getClientFeatures(
query: IFeatureToggleCacheQuery,
): Promise<FeatureConfigurationCacheClient[]> {
query: IFeatureToggleDeltaQuery,
): Promise<FeatureConfigurationDeltaClient[]> {
const result =
await this.clientFeatureToggleCacheReadModel.getAll(query);
await this.clientFeatureToggleDeltaReadModel.getAll(query);
return result;
}
}

View File

@ -1,30 +1,30 @@
import { ClientFeatureToggleCache } from './client-feature-toggle-cache';
import { ClientFeatureToggleDelta } from './client-feature-toggle-delta';
import EventStore from '../../events/event-store';
import ConfigurationRevisionService from '../../feature-toggle/configuration-revision-service';
import type { IUnleashConfig } from '../../../types';
import type { Db } from '../../../db/db';
import ClientFeatureToggleCacheReadModel from './client-feature-toggle-cache-read-model';
import ClientFeatureToggleDeltaReadModel from './client-feature-toggle-delta-read-model';
export const createClientFeatureToggleCache = (
export const createClientFeatureToggleDelta = (
db: Db,
config: IUnleashConfig,
): ClientFeatureToggleCache => {
): ClientFeatureToggleDelta => {
const { getLogger, eventBus, flagResolver } = config;
const eventStore = new EventStore(db, getLogger);
const clientFeatureToggleCacheReadModel =
new ClientFeatureToggleCacheReadModel(db, eventBus);
const clientFeatureToggleDeltaReadModel =
new ClientFeatureToggleDeltaReadModel(db, eventBus);
const configurationRevisionService =
ConfigurationRevisionService.getInstance({ eventStore }, config);
const clientFeatureToggleCache = new ClientFeatureToggleCache(
clientFeatureToggleCacheReadModel,
const clientFeatureToggleDelta = new ClientFeatureToggleDelta(
clientFeatureToggleDeltaReadModel,
eventStore,
configurationRevisionService,
flagResolver,
);
return clientFeatureToggleCache;
return clientFeatureToggleDelta;
};

View File

@ -1,5 +1,5 @@
import { RevisionCache } from './revision-cache';
import type { Revision } from './client-feature-toggle-cache';
import { RevisionDelta } from './revision-delta';
import type { Revision } from './client-feature-toggle-delta';
describe('RevisionCache', () => {
it('should create a new base when trying to add a new revision at the max limit', () => {
@ -63,7 +63,7 @@ describe('RevisionCache', () => {
];
const maxLength = 2;
const deltaCache = new RevisionCache(initialRevisions, maxLength);
const deltaCache = new RevisionDelta(initialRevisions, maxLength);
// Add a new revision to trigger changeBase
deltaCache.addRevision({

View File

@ -1,4 +1,4 @@
import type { Revision } from './client-feature-toggle-cache';
import type { Revision } from './client-feature-toggle-delta';
const mergeWithoutDuplicates = (arr1: any[], arr2: any[]) => {
const map = new Map();
@ -8,41 +8,41 @@ const mergeWithoutDuplicates = (arr1: any[], arr2: any[]) => {
return Array.from(map.values());
};
export class RevisionCache {
private cache: Revision[];
export class RevisionDelta {
private delta: Revision[];
private maxLength: number;
constructor(data: Revision[] = [], maxLength: number = 100) {
this.cache = data;
this.delta = data;
this.maxLength = maxLength;
}
public addRevision(revision: Revision): void {
if (this.cache.length >= this.maxLength) {
if (this.delta.length >= this.maxLength) {
this.changeBase();
}
this.cache = [...this.cache, revision];
this.delta = [...this.delta, revision];
}
public getRevisions(): Revision[] {
return this.cache;
return this.delta;
}
public hasRevision(revisionId: number): boolean {
return this.cache.some(
return this.delta.some(
(revision) => revision.revisionId === revisionId,
);
}
private changeBase(): void {
if (!(this.cache.length >= 2)) return;
const base = this.cache[0];
const newBase = this.cache[1];
if (!(this.delta.length >= 2)) return;
const base = this.delta[0];
const newBase = this.delta[1];
newBase.removed = mergeWithoutDuplicates(base.removed, newBase.removed);
newBase.updated = mergeWithoutDuplicates(base.updated, newBase.updated);
this.cache = [newBase, ...this.cache.slice(2)];
this.delta = [newBase, ...this.delta.slice(2)];
}
}

View File

@ -3,7 +3,7 @@ import FeatureController from '../../features/client-feature-toggles/client-feat
import MetricsController from '../../features/metrics/instance/metrics';
import RegisterController from '../../features/metrics/instance/register';
import type { IUnleashConfig, IUnleashServices } from '../../types';
import ClientFeatureToggleDeltaController from '../../features/client-feature-toggles/cache/client-feature-toggle-cache-controller';
import ClientFeatureToggleDeltaController from '../../features/client-feature-toggles/delta/client-feature-toggle-delta-controller';
export default class ClientApi extends Controller {
constructor(config: IUnleashConfig, services: IUnleashServices) {

View File

@ -345,7 +345,7 @@ export interface IFeatureToggleQuery {
inlineSegmentConstraints?: boolean;
}
export interface IFeatureToggleCacheQuery extends IFeatureToggleQuery {
export interface IFeatureToggleDeltaQuery extends IFeatureToggleQuery {
toggleNames?: string[];
environment: string;
}