1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-09-19 17:52:45 +02:00

chore: support different etags per environment

This commit is contained in:
Gastón Fournier 2025-08-19 15:40:04 +02:00
parent 7f6b09fa1e
commit 6569d72d79
No known key found for this signature in database
GPG Key ID: AF45428626E17A8E
4 changed files with 64 additions and 43 deletions

View File

@ -359,7 +359,9 @@ export default class FeatureController extends Controller {
async calculateMeta(query: IFeatureToggleQuery): Promise<IMeta> { async calculateMeta(query: IFeatureToggleQuery): Promise<IMeta> {
// TODO: We will need to standardize this to be able to implement this a cross languages (Edge in Rust?). // TODO: We will need to standardize this to be able to implement this a cross languages (Edge in Rust?).
const revisionId = const revisionId =
await this.configurationRevisionService.getMaxRevisionId(); await this.configurationRevisionService.getMaxRevisionId(
environment,
);
// TODO: We will need to standardize this to be able to implement this a cross languages (Edge in Rust?). // TODO: We will need to standardize this to be able to implement this a cross languages (Edge in Rust?).
const queryHash = hashSum(query); const queryHash = hashSum(query);

View File

@ -191,29 +191,37 @@ export class EventStore implements IEventStore {
} }
} }
async getMaxRevisionId(largerThan: number = 0): Promise<number> { private typeIsInteresting = (builder: Knex.QueryBuilder) =>
const stopTimer = this.metricTimer('getMaxRevisionId');
const row = await this.db(TABLE)
.max('id')
.where((builder) =>
builder builder
.andWhere((inner) => .andWhere((inner) =>
inner inner
.whereNotNull('feature_name') .whereNotNull('feature_name')
.whereNotIn('type', [ .whereNotIn('type', [FEATURE_CREATED, FEATURE_TAGGED])
FEATURE_CREATED,
FEATURE_TAGGED,
])
.whereNot('type', 'LIKE', 'change-%'), .whereNot('type', 'LIKE', 'change-%'),
) )
.orWhereIn('type', [ .orWhereIn('type', [
SEGMENT_UPDATED, SEGMENT_UPDATED,
FEATURE_IMPORT, FEATURE_IMPORT,
FEATURES_IMPORTED, FEATURES_IMPORTED,
]), SEGMENT_CREATED,
) SEGMENT_DELETED,
.andWhere('id', '>=', largerThan) ]);
.first();
async getMaxRevisionId(
largerThan: number = 0,
environment?: string,
): Promise<number> {
const stopTimer = this.metricTimer('getMaxRevisionId');
const query = this.db(TABLE)
.max('id')
.where(this.typeIsInteresting)
.andWhere('id', '>=', largerThan);
if (environment) {
query.where('environment', environment);
}
const row = await query.first();
stopTimer(); stopTimer();
return row?.max ?? 0; return row?.max ?? 0;
} }
@ -225,27 +233,11 @@ export class EventStore implements IEventStore {
.from(TABLE) .from(TABLE)
.where('id', '>', start) .where('id', '>', start)
.andWhere('id', '<=', end) .andWhere('id', '<=', end)
.andWhere((builder) => .andWhere(this.typeIsInteresting)
builder
.andWhere((inner) =>
inner
.whereNotNull('feature_name')
.whereNotIn('type', [
FEATURE_CREATED,
FEATURE_TAGGED,
]),
)
.orWhereIn('type', [
SEGMENT_UPDATED,
FEATURE_IMPORT,
FEATURES_IMPORTED,
SEGMENT_CREATED,
SEGMENT_DELETED,
]),
)
.orderBy('id', 'asc'); .orderBy('id', 'asc');
const rows = await query; const rows = await query;
stopTimer();
return rows.map(this.rowToEvent); return rows.map(this.rowToEvent);
} }

View File

@ -18,6 +18,8 @@ export default class ConfigurationRevisionService extends EventEmitter {
private revisionId: number; private revisionId: number;
private maxRevisionId: Record<string, number> = {};
private flagResolver: IFlagResolver; private flagResolver: IFlagResolver;
private constructor( private constructor(
@ -51,7 +53,17 @@ export default class ConfigurationRevisionService extends EventEmitter {
return ConfigurationRevisionService.instance; return ConfigurationRevisionService.instance;
} }
async getMaxRevisionId(): Promise<number> { async getMaxRevisionId(environment?: string): Promise<number> {
if (environment && !this.maxRevisionId[environment]) {
await this.updateMaxEnvironmentRevisionId(environment);
}
if (
environment &&
this.maxRevisionId[environment] &&
this.maxRevisionId[environment] > 0
) {
return this.maxRevisionId[environment];
}
if (this.revisionId > 0) { if (this.revisionId > 0) {
return this.revisionId; return this.revisionId;
} else { } else {
@ -59,6 +71,18 @@ export default class ConfigurationRevisionService extends EventEmitter {
} }
} }
async updateMaxEnvironmentRevisionId(environment: string): Promise<number> {
const envRevisionId = await this.eventStore.getMaxRevisionId(
this.revisionId,
environment,
);
if (this.maxRevisionId[environment] < envRevisionId) {
this.maxRevisionId[environment] = envRevisionId;
}
return this.maxRevisionId[environment];
}
async updateMaxRevisionId(emit: boolean = true): Promise<number> { async updateMaxRevisionId(emit: boolean = true): Promise<number> {
if (this.flagResolver.isEnabled('disableUpdateMaxRevisionId')) { if (this.flagResolver.isEnabled('disableUpdateMaxRevisionId')) {
return 0; return 0;

View File

@ -38,7 +38,10 @@ export interface IEventStore
queryParams: IQueryParam[], queryParams: IQueryParam[],
options?: { withIp?: boolean }, options?: { withIp?: boolean },
): Promise<IEvent[]>; ): Promise<IEvent[]>;
getMaxRevisionId(currentMax?: number): Promise<number>; getMaxRevisionId(
currentMax?: number,
environment?: string,
): Promise<number>;
getRevisionRange(start: number, end: number): Promise<IEvent[]>; getRevisionRange(start: number, end: number): Promise<IEvent[]>;
query(operations: IQueryOperations[]): Promise<IEvent[]>; query(operations: IQueryOperations[]): Promise<IEvent[]>;
queryCount(operations: IQueryOperations[]): Promise<number>; queryCount(operations: IQueryOperations[]): Promise<number>;