1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-06-27 01:19:00 +02:00

fix: make revision id not be so reactive (#10032)

Unleash is being too reactive to events inside Unleash. We should not
update etag if feature is created or tag is added to feature.

This PR adds this condition and adds test for it.
This commit is contained in:
Jaanus Sellin 2025-05-27 16:16:26 +03:00 committed by GitHub
parent 3e57c4803c
commit 2879ce9dd6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 99 additions and 12 deletions

View File

@ -63,9 +63,9 @@ exports[`should match snapshot from /api/client/features 1`] = `
},
],
"meta": {
"etag": ""61824cd0:11"",
"etag": ""61824cd0:20"",
"queryHash": "61824cd0",
"revisionId": 11,
"revisionId": 20,
},
"query": {
"environment": "default",

View File

@ -1,5 +1,7 @@
import {
FEATURE_CREATED,
FEATURE_IMPORT,
FEATURE_TAGGED,
FEATURES_IMPORTED,
type IBaseEvent,
type IEvent,
@ -196,7 +198,14 @@ class EventStore implements IEventStore {
.max('id')
.where((builder) =>
builder
.whereNotNull('feature_name')
.andWhere((inner) =>
inner
.whereNotNull('feature_name')
.whereNotIn('type', [
FEATURE_CREATED,
FEATURE_TAGGED,
]),
)
.orWhereIn('type', [
SEGMENT_UPDATED,
FEATURE_IMPORT,
@ -216,7 +225,14 @@ class EventStore implements IEventStore {
.andWhere('id', '<=', end)
.andWhere((builder) =>
builder
.whereNotNull('feature_name')
.andWhere((inner) =>
inner
.whereNotNull('feature_name')
.whereNotIn('type', [
FEATURE_CREATED,
FEATURE_TAGGED,
]),
)
.orWhereIn('type', [
SEGMENT_UPDATED,
FEATURE_IMPORT,

View File

@ -152,9 +152,9 @@ describe.each([
.expect(200);
if (etagVariant.feature_enabled) {
expect(res.headers.etag).toBe(`"61824cd0:17:${etagVariant.name}"`);
expect(res.headers.etag).toBe(`"61824cd0:16:${etagVariant.name}"`);
expect(res.body.meta.etag).toBe(
`"61824cd0:17:${etagVariant.name}"`,
`"61824cd0:16:${etagVariant.name}"`,
);
} else {
expect(res.headers.etag).toBe('"61824cd0:16"');
@ -169,9 +169,9 @@ describe.each([
.expect(etagVariant.feature_enabled ? 200 : 304);
if (etagVariant.feature_enabled) {
expect(res.headers.etag).toBe(`"61824cd0:17:${etagVariant.name}"`);
expect(res.headers.etag).toBe(`"61824cd0:16:${etagVariant.name}"`);
expect(res.body.meta.etag).toBe(
`"61824cd0:17:${etagVariant.name}"`,
`"61824cd0:16:${etagVariant.name}"`,
);
}
});
@ -193,13 +193,13 @@ describe.each([
.expect(200);
if (etagVariant.feature_enabled) {
expect(res.headers.etag).toBe(`"61824cd0:17:${etagVariant.name}"`);
expect(res.headers.etag).toBe(`"61824cd0:16:${etagVariant.name}"`);
expect(res.body.meta.etag).toBe(
`"61824cd0:17:${etagVariant.name}"`,
`"61824cd0:16:${etagVariant.name}"`,
);
} else {
expect(res.headers.etag).toBe('"61824cd0:17"');
expect(res.body.meta.etag).toBe('"61824cd0:17"');
expect(res.headers.etag).toBe('"61824cd0:16"');
expect(res.body.meta.etag).toBe('"61824cd0:16"');
}
});
});

View File

@ -2,11 +2,16 @@ import {
APPLICATION_CREATED,
FEATURE_CREATED,
FEATURE_DELETED,
FEATURE_TAGGED,
FEATURE_UPDATED,
SEGMENT_UPDATED,
type IEvent,
} from '../../../lib/events/index.js';
import {
FeatureCreatedEvent,
FeatureDeletedEvent,
FeatureTaggedEvent,
FeatureUpdatedEvent,
} from '../../../lib/types/index.js';
import dbInit, { type ITestDb } from '../helpers/database-init.js';
@ -247,3 +252,69 @@ test('Should get all events of type', async () => {
});
expect(featureDeletedEvents).toHaveLength(3);
});
test('getMaxRevisionId should exclude FEATURE_CREATED and FEATURE_TAGGED events', async () => {
const featureName = 'test-feature';
const project = 'test-project';
const featureCreatedEvent = new FeatureCreatedEvent({
project,
featureName,
auditUser: testAudit,
data: { name: featureName, project },
});
const featureTaggedEvent = new FeatureTaggedEvent({
project,
featureName,
auditUser: testAudit,
data: { type: 'simple', value: 'test-tag' },
});
const featureUpdatedEvent = new FeatureUpdatedEvent({
project,
featureName,
auditUser: testAudit,
data: { name: featureName, enabled: false },
});
const segmentUpdatedEvent = {
type: SEGMENT_UPDATED,
createdBy: testAudit.username,
createdByUserId: testAudit.id,
ip: testAudit.ip,
data: { id: 1, name: 'test-segment' },
};
await eventStore.store(featureCreatedEvent);
const maxRevisionAfterCreated = await eventStore.getMaxRevisionId();
await eventStore.store(featureTaggedEvent);
const maxRevisionAfterTagged = await eventStore.getMaxRevisionId();
await eventStore.store(featureUpdatedEvent);
const maxRevisionAfterUpdated = await eventStore.getMaxRevisionId();
await eventStore.store(segmentUpdatedEvent);
const maxRevisionAfterSegment = await eventStore.getMaxRevisionId();
const allEvents = await eventStore.getAll();
const createdEvent = allEvents.find((e) => e.type === FEATURE_CREATED);
const taggedEvent = allEvents.find((e) => e.type === FEATURE_TAGGED);
const updatedEvent = allEvents.find((e) => e.type === FEATURE_UPDATED);
const segmentEvent = allEvents.find((e) => e.type === SEGMENT_UPDATED);
expect(maxRevisionAfterCreated).toBe(0);
expect(maxRevisionAfterTagged).toBe(0);
expect(maxRevisionAfterUpdated).toBe(updatedEvent!.id);
expect(maxRevisionAfterSegment).toBe(segmentEvent!.id);
expect(createdEvent).toBeDefined();
expect(taggedEvent).toBeDefined();
expect(updatedEvent).toBeDefined();
expect(segmentEvent).toBeDefined();
expect(updatedEvent!.id).toBeGreaterThan(createdEvent!.id);
expect(updatedEvent!.id).toBeGreaterThan(taggedEvent!.id);
expect(segmentEvent!.id).toBeGreaterThan(updatedEvent!.id);
});