jinaga-server 3.7.1 → 3.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -119,8 +119,19 @@ class PostgresKeystore {
|
|
|
119
119
|
generateKeyPair(connection, userIdentity) {
|
|
120
120
|
return __awaiter(this, void 0, void 0, function* () {
|
|
121
121
|
const keyPair = (0, jinaga_1.generateKeyPair)();
|
|
122
|
-
yield connection.query(`INSERT INTO ${this.schema}.user (provider, user_identifier, private_key, public_key)
|
|
123
|
-
|
|
122
|
+
const { rows } = yield connection.query(`INSERT INTO ${this.schema}.user (provider, user_identifier, private_key, public_key)
|
|
123
|
+
VALUES ($1, $2, $3, $4)
|
|
124
|
+
ON CONFLICT (user_identifier, provider) DO NOTHING
|
|
125
|
+
RETURNING 1`, [userIdentity.provider, userIdentity.id, keyPair.privatePem, keyPair.publicPem]);
|
|
126
|
+
if (rows.length === 1) {
|
|
127
|
+
return keyPair;
|
|
128
|
+
}
|
|
129
|
+
// Lost the race: a concurrent request inserted this user's key pair
|
|
130
|
+
// between our SELECT and INSERT. The ON CONFLICT clause suppressed the
|
|
131
|
+
// unique-violation (23505) on ux_user, so read back the winner's key
|
|
132
|
+
// pair instead of surfacing a duplicate-key error. selectOrInsertKeyPair
|
|
133
|
+
// will now find exactly one row.
|
|
134
|
+
return this.selectOrInsertKeyPair(connection, userIdentity);
|
|
124
135
|
});
|
|
125
136
|
}
|
|
126
137
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-keystore.js","sourceRoot":"","sources":["../../src/postgres/postgres-keystore.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"postgres-keystore.js","sourceRoot":"","sources":["../../src/postgres/postgres-keystore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const { Pool } = require('pg');
|
|
2
|
+
const { PostgresKeystore } = require('jinaga-server');
|
|
3
|
+
|
|
4
|
+
const host = "db";
|
|
5
|
+
const connectionString = `postgresql://dev:devpw@${host}:5432/integrationtest`;
|
|
6
|
+
|
|
7
|
+
const pool = new Pool({
|
|
8
|
+
connectionString
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
describe('PostgresKeystore concurrency race', () => {
|
|
12
|
+
afterAll(async () => {
|
|
13
|
+
await pool.end();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
beforeEach(async () => {
|
|
17
|
+
// Ensure no row exists yet for the test identities so the
|
|
18
|
+
// select-then-insert path is exercised from a clean state.
|
|
19
|
+
await pool.query(`DELETE FROM public."user" WHERE provider = $1`, ['test-provider']);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should not fail when two concurrent requests create the same user key pair', async () => {
|
|
23
|
+
const userIdentity = { provider: 'test-provider', id: 'race-user-1' };
|
|
24
|
+
|
|
25
|
+
// Two separate keystore instances model two concurrent requests that
|
|
26
|
+
// each build a fresh runtime (empty in-process cache), as described in
|
|
27
|
+
// issue #177. Both SELECT zero rows and race to INSERT.
|
|
28
|
+
const keystoreA = new PostgresKeystore(pool, 'public');
|
|
29
|
+
const keystoreB = new PostgresKeystore(pool, 'public');
|
|
30
|
+
|
|
31
|
+
const [factA, factB] = await Promise.all([
|
|
32
|
+
keystoreA.getOrCreateUserFact(userIdentity),
|
|
33
|
+
keystoreB.getOrCreateUserFact(userIdentity)
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
// Both requests succeed and observe the same public key (the winner's).
|
|
37
|
+
expect(factA.type).toBe('Jinaga.User');
|
|
38
|
+
expect(factB.type).toBe('Jinaga.User');
|
|
39
|
+
expect(factA.fields.publicKey).toBe(factB.fields.publicKey);
|
|
40
|
+
expect(factA.hash).toBe(factB.hash);
|
|
41
|
+
|
|
42
|
+
// Exactly one row was persisted for the identity.
|
|
43
|
+
const { rows } = await pool.query(
|
|
44
|
+
`SELECT public_key FROM public."user" WHERE provider = $1 AND user_identifier = $2`,
|
|
45
|
+
[userIdentity.provider, userIdentity.id]);
|
|
46
|
+
expect(rows.length).toBe(1);
|
|
47
|
+
expect(rows[0].public_key).toBe(factA.fields.publicKey);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should tolerate many concurrent creations of the same identity', async () => {
|
|
51
|
+
const userIdentity = { provider: 'test-provider', id: 'race-user-2' };
|
|
52
|
+
|
|
53
|
+
// A larger fan-out of fresh runtimes all racing on the same identity.
|
|
54
|
+
const keystores = Array.from({ length: 8 }, () => new PostgresKeystore(pool, 'public'));
|
|
55
|
+
|
|
56
|
+
const facts = await Promise.all(
|
|
57
|
+
keystores.map(keystore => keystore.getOrCreateUserFact(userIdentity)));
|
|
58
|
+
|
|
59
|
+
// Every caller resolves to the same key pair.
|
|
60
|
+
const publicKeys = new Set(facts.map(fact => fact.fields.publicKey));
|
|
61
|
+
expect(publicKeys.size).toBe(1);
|
|
62
|
+
|
|
63
|
+
const { rows } = await pool.query(
|
|
64
|
+
`SELECT public_key FROM public."user" WHERE provider = $1 AND user_identifier = $2`,
|
|
65
|
+
[userIdentity.provider, userIdentity.id]);
|
|
66
|
+
expect(rows.length).toBe(1);
|
|
67
|
+
});
|
|
68
|
+
});
|
package/package.json
CHANGED
|
@@ -114,9 +114,21 @@ export class PostgresKeystore implements Keystore {
|
|
|
114
114
|
|
|
115
115
|
private async generateKeyPair(connection: PoolClient, userIdentity: UserIdentity): Promise<KeyPair> {
|
|
116
116
|
const keyPair = generateKeyPair();
|
|
117
|
-
|
|
117
|
+
const { rows } = await connection.query(
|
|
118
|
+
`INSERT INTO ${this.schema}.user (provider, user_identifier, private_key, public_key)
|
|
119
|
+
VALUES ($1, $2, $3, $4)
|
|
120
|
+
ON CONFLICT (user_identifier, provider) DO NOTHING
|
|
121
|
+
RETURNING 1`,
|
|
118
122
|
[userIdentity.provider, userIdentity.id, keyPair.privatePem, keyPair.publicPem]);
|
|
119
|
-
|
|
123
|
+
if (rows.length === 1) {
|
|
124
|
+
return keyPair;
|
|
125
|
+
}
|
|
126
|
+
// Lost the race: a concurrent request inserted this user's key pair
|
|
127
|
+
// between our SELECT and INSERT. The ON CONFLICT clause suppressed the
|
|
128
|
+
// unique-violation (23505) on ux_user, so read back the winner's key
|
|
129
|
+
// pair instead of surfacing a duplicate-key error. selectOrInsertKeyPair
|
|
130
|
+
// will now find exactly one row.
|
|
131
|
+
return this.selectOrInsertKeyPair(connection, userIdentity);
|
|
120
132
|
}
|
|
121
133
|
}
|
|
122
134
|
|