mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02:00
fix: prevent deadlock for batchinserting usage metrics (#1100)
* fix: prevent deadlock for batchinserting usage metrics In client metrics v2 we utilize postgres to count the usage across a few dimentions (featureName, app_name, environment). It turns out that if the UPDATE values are not executed in a predictable order we can end up in a deadlock scenario with postgresql. In this fix we thus sort the metrics on the feature_name, app_name and envrionment, to make sure they always are executed in a predictabel order, and thus avoiding independent inserts colliding in to a deadlock waiting for eachother. * fix: tests cannot assume order
This commit is contained in:
parent
f2b3325d42
commit
f0895cf653
@ -116,9 +116,17 @@ export class ClientMetricsStoreV2 implements IClientMetricsStoreV2 {
|
|||||||
return prev;
|
return prev;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
// Sort the rows to avoid deadlocks
|
||||||
|
const batchRow = Object.values<ClientMetricsEnvTable>(batch).sort(
|
||||||
|
(a, b) =>
|
||||||
|
a.feature_name.localeCompare(b.feature_name) ||
|
||||||
|
a.app_name.localeCompare(b.app_name) ||
|
||||||
|
a.environment.localeCompare(b.environment),
|
||||||
|
);
|
||||||
|
|
||||||
// Consider rewriting to SQL batch!
|
// Consider rewriting to SQL batch!
|
||||||
const insert = this.db<ClientMetricsEnvTable>(TABLE)
|
const insert = this.db<ClientMetricsEnvTable>(TABLE)
|
||||||
.insert(Object.values(batch))
|
.insert(batchRow)
|
||||||
.toQuery();
|
.toQuery();
|
||||||
|
|
||||||
const query = `${insert.toString()} ON CONFLICT (feature_name, app_name, environment, timestamp) DO UPDATE SET "yes" = "client_metrics_env"."yes" + EXCLUDED.yes, "no" = "client_metrics_env"."no" + EXCLUDED.no`;
|
const query = `${insert.toString()} ON CONFLICT (feature_name, app_name, environment, timestamp) DO UPDATE SET "yes" = "client_metrics_env"."yes" + EXCLUDED.yes, "no" = "client_metrics_env"."no" + EXCLUDED.no`;
|
||||||
|
@ -155,14 +155,19 @@ test('should return toggle summary', async () => {
|
|||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
|
const test = demo.lastHourUsage.find((u) => u.environment === 'test');
|
||||||
|
const defaultEnv = demo.lastHourUsage.find(
|
||||||
|
(u) => u.environment === 'default',
|
||||||
|
);
|
||||||
|
|
||||||
expect(demo.featureName).toBe('demo');
|
expect(demo.featureName).toBe('demo');
|
||||||
expect(demo.lastHourUsage).toHaveLength(2);
|
expect(demo.lastHourUsage).toHaveLength(2);
|
||||||
expect(demo.lastHourUsage[0].environment).toBe('default');
|
expect(test.environment).toBe('test');
|
||||||
expect(demo.lastHourUsage[0].yes).toBe(5);
|
expect(test.yes).toBe(2);
|
||||||
expect(demo.lastHourUsage[0].no).toBe(4);
|
expect(test.no).toBe(6);
|
||||||
expect(demo.lastHourUsage[1].environment).toBe('test');
|
expect(defaultEnv.environment).toBe('default');
|
||||||
expect(demo.lastHourUsage[1].yes).toBe(2);
|
expect(defaultEnv.yes).toBe(5);
|
||||||
expect(demo.lastHourUsage[1].no).toBe(6);
|
expect(defaultEnv.no).toBe(4);
|
||||||
expect(demo.seenApplications).toStrictEqual(['backend-api', 'web']);
|
expect(demo.seenApplications).toStrictEqual(['backend-api', 'web']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -219,13 +224,18 @@ test('should only include last hour of metrics return toggle summary', async ()
|
|||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
|
const test = demo.lastHourUsage.find((u) => u.environment === 'test');
|
||||||
|
const defaultEnv = demo.lastHourUsage.find(
|
||||||
|
(u) => u.environment === 'default',
|
||||||
|
);
|
||||||
|
|
||||||
expect(demo.featureName).toBe('demo');
|
expect(demo.featureName).toBe('demo');
|
||||||
expect(demo.lastHourUsage).toHaveLength(2);
|
expect(demo.lastHourUsage).toHaveLength(2);
|
||||||
expect(demo.lastHourUsage[0].environment).toBe('default');
|
expect(defaultEnv.environment).toBe('default');
|
||||||
expect(demo.lastHourUsage[0].yes).toBe(5);
|
expect(defaultEnv.yes).toBe(5);
|
||||||
expect(demo.lastHourUsage[0].no).toBe(4);
|
expect(defaultEnv.no).toBe(4);
|
||||||
expect(demo.lastHourUsage[1].environment).toBe('test');
|
expect(test.environment).toBe('test');
|
||||||
expect(demo.lastHourUsage[1].yes).toBe(2);
|
expect(test.yes).toBe(2);
|
||||||
expect(demo.lastHourUsage[1].no).toBe(6);
|
expect(test.no).toBe(6);
|
||||||
expect(demo.seenApplications).toStrictEqual(['backend-api', 'web']);
|
expect(demo.seenApplications).toStrictEqual(['backend-api', 'web']);
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user