Skip to content

Commit

Permalink
refactor: Improve db sync performance (#266)
Browse files Browse the repository at this point in the history
* Improved perf-test

* Added eventsCache

* Removed confirmedCache and unconfirmedCache

* Simplified verifyDID

* Added tests for generateDoc
  • Loading branch information
macterra authored Aug 1, 2024
1 parent 9fd81fd commit 8a3833d
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 37 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"description": "",
"main": "gatekeeper-api.js",
"scripts": {
"start": "node gatekeeper-api.js",
"gatekeeper": "node src/gatekeeper-api.js",
"keymaster": "node src/keymaster-api.js",
"test": "node --experimental-vm-modules node_modules/.bin/jest --runInBand --verbose --coverage",
"lint": "eslint ."
},
Expand Down
41 changes: 36 additions & 5 deletions src/admin-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ program
});

program
.command('perf-test')
.command('perf-test [full]')
.description('DID resolution performance test')
.action(async () => {
.action(async (full) => {
try {
console.time('getDIDs');
const dids = await gatekeeper.getDIDs();
Expand All @@ -76,17 +76,48 @@ program
}
console.timeEnd('resolveDID(did, { confirm: true })');

let batch = [];
console.time('getDIDs({ dids: batch, confirm: true, resolve: true })');
for (const did of dids) {
batch.push(did);

if (batch.length > 99) {
await gatekeeper.getDIDs({ dids: batch, confirm: true, resolve: true });
batch = [];
}
}
await gatekeeper.getDIDs({ dids: batch, confirm: true, resolve: true });
console.timeEnd('getDIDs({ dids: batch, confirm: true, resolve: true })');

console.time('resolveDID(did, { confirm: false })');
for (const did of dids) {
await gatekeeper.resolveDID(did, { confirm: false });
}
console.timeEnd('resolveDID(did, { confirm: false })');

console.time('resolveDID(did, { verify: true })');
console.time('getDIDs({ dids: batch, confirm: false, resolve: true })');
for (const did of dids) {
await gatekeeper.resolveDID(did, { verify: true });
batch.push(did);

if (batch.length > 99) {
await gatekeeper.getDIDs({ dids: batch, confirm: false, resolve: true });
batch = [];
}
}
await gatekeeper.getDIDs({ dids: batch, confirm: false, resolve: true });
console.timeEnd('getDIDs({ dids: batch, confirm: false, resolve: true })');

console.time('exportDIDs');
await gatekeeper.exportDIDs(dids);
console.timeEnd('exportDIDs');

if (full) {
console.time('resolveDID(did, { verify: true })');
for (const did of dids) {
await gatekeeper.resolveDID(did, { verify: true });
}
console.timeEnd('resolveDID(did, { verify: true })');
}
console.timeEnd('resolveDID(did, { verify: true })');
}
catch (error) {
console.error(error);
Expand Down
53 changes: 23 additions & 30 deletions src/gatekeeper-lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ const validRegistries = ['local', 'hyperswarm', 'TESS'];
let db = null;
let helia = null;
let ipfs = null;

const confirmedCache = {};
const unconfirmedCache = {};
let eventsCache = {};

export async function listRegistries() {
return validRegistries;
Expand All @@ -37,7 +35,6 @@ export async function stop() {

export async function verifyDID(did) {
await resolveDID(did, { verify: true });
await resolveDID(did, { confirm: true });
}

export async function verifyDb(chatty = true) {
Expand All @@ -64,6 +61,7 @@ export async function verifyDb(chatty = true) {
}
invalid += 1;
await db.deleteEvents(did);
delete eventsCache[did];
}
}

Expand All @@ -77,6 +75,7 @@ export async function verifyDb(chatty = true) {
// For testing purposes
export async function resetDb() {
await db.resetDb();
eventsCache = {};
}

export async function anchorSeed(seed) {
Expand Down Expand Up @@ -187,7 +186,7 @@ export async function createDID(operation) {
}
}

async function generateDoc(anchor) {
export async function generateDoc(anchor) {
let doc = {};
try {
if (!anchor?.mdip) {
Expand Down Expand Up @@ -288,19 +287,22 @@ async function verifyUpdate(operation, doc) {
return cipher.verifySig(msgHash, signature.value, publicJwk);
}

export async function resolveDID(did, { atTime, atVersion, confirm, verify } = {}) {
const confirmedCacheable = !!confirm && !atTime && !atVersion;
const unconfirmedCacheable = !confirm && !atTime && !atVersion;
async function getEvents(did) {
let events = eventsCache[did];

if (confirmedCacheable && !verify && confirmedCache[did]) {
return JSON.parse(JSON.stringify(confirmedCache[did]));
}
if (!events) {
events = await db.getEvents(did);

if (unconfirmedCacheable && !verify && unconfirmedCache[did]) {
return JSON.parse(JSON.stringify(unconfirmedCache[did]));
if (events.length > 0) {
eventsCache[did] = events;
}
}

const events = await db.getEvents(did);
return JSON.parse(JSON.stringify(events));
}

export async function resolveDID(did, { atTime, atVersion, confirm, verify } = {}) {
const events = await getEvents(did);

if (events.length === 0) {
throw new Error(exceptions.INVALID_DID);
Expand Down Expand Up @@ -382,14 +384,6 @@ export async function resolveDID(did, { atTime, atVersion, confirm, verify } = {
}
}

if (confirmedCacheable) {
confirmedCache[did] = doc;
}

if (unconfirmedCacheable) {
unconfirmedCache[did] = doc;
}

return JSON.parse(JSON.stringify(doc));
}

Expand All @@ -411,8 +405,7 @@ export async function updateDID(operation) {
operation: operation
});

delete confirmedCache[operation.did];
delete unconfirmedCache[operation.did];
delete eventsCache[operation.did];

if (registry === 'local') {
return true;
Expand Down Expand Up @@ -469,14 +462,14 @@ export async function getDIDs({ dids, updatedAfter, updatedBefore, confirm, reso
}

export async function exportDID(did) {
return await db.getEvents(did);
return await getEvents(did);
}

export async function exportDIDs(dids) {
const batch = [];

for (const did of dids) {
batch.push(await db.getEvents(did));
batch.push(await getEvents(did));
}

return batch;
Expand All @@ -489,6 +482,7 @@ export async function removeDIDs(dids) {

for (const did of dids) {
await db.deleteEvents(did);
delete eventsCache[did];
}

return true;
Expand Down Expand Up @@ -582,8 +576,7 @@ export async function importEvent(event) {
current[index] = event;

db.setEvents(did, current);
delete confirmedCache[did];
delete unconfirmedCache[did];
delete eventsCache[did];
return true;
}

Expand All @@ -596,8 +589,8 @@ export async function importEvent(event) {
throw new Error(exceptions.INVALID_OPERATION);
}

delete confirmedCache[did];
delete unconfirmedCache[did];
delete eventsCache[did];

return true;
}

Expand Down
98 changes: 98 additions & 0 deletions src/gatekeeper.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,104 @@ async function createAssetOp(agent, keypair, registry = 'local') {
};
}

describe('generateDoc', () => {
it('should generate an agent doc from a valid anchor', async () => {
const keypair = cipher.generateRandomJwk();
const agentOp = await createAgentOp(keypair);
const doc = await gatekeeper.generateDoc(agentOp);
const expected = {
// eslint-disable-next-line
"@context": "https://w3id.org/did-resolution/v1",
didDocument: {
"@context": [
// eslint-disable-next-line
"https://www.w3.org/ns/did/v1",
],
authentication: [
"#key-1",
],
id: expect.any(String),
verificationMethod: [
{
controller: expect.any(String),
id: "#key-1",
publicKeyJwk: agentOp.publicJwk,
type: "EcdsaSecp256k1VerificationKey2019",
},
],
},
didDocumentData: {},
didDocumentMetadata: {
created: expect.any(String),
},
mdip: agentOp.mdip,
};

expect(doc).toStrictEqual(expected);
});

it('should generate an asset doc from a valid anchor', async () => {
const keypair = cipher.generateRandomJwk();
const agentOp = await createAgentOp(keypair);
const agent = await gatekeeper.createDID(agentOp);
const assetOp = await createAssetOp(agent, keypair);
const doc = await gatekeeper.generateDoc(assetOp);
const expected = {
// eslint-disable-next-line
"@context": "https://w3id.org/did-resolution/v1",
didDocument: {
"@context": [
// eslint-disable-next-line
"https://www.w3.org/ns/did/v1",
],
id: expect.any(String),
controller: agent,
},
didDocumentData: assetOp.data,
didDocumentMetadata: {
created: expect.any(String),
},
mdip: assetOp.mdip,
};

expect(doc).toStrictEqual(expected);
});

it('should return an empty doc if mdip missing from anchor', async () => {
const keypair = cipher.generateRandomJwk();
const agentOp = await createAgentOp(keypair);
delete agentOp.mdip;
const doc = await gatekeeper.generateDoc(agentOp);

expect(doc).toStrictEqual({});
});

it('should return an empty doc if mdip version invalid', async () => {
const keypair = cipher.generateRandomJwk();
const agentOp = await createAgentOp(keypair, 0);
const doc = await gatekeeper.generateDoc(agentOp);

expect(doc).toStrictEqual({});
});

it('should return an empty doc if mdip type invalid', async () => {
const keypair = cipher.generateRandomJwk();
const agentOp = await createAgentOp(keypair);
agentOp.mdip.type = 'mock';
const doc = await gatekeeper.generateDoc(agentOp);

expect(doc).toStrictEqual({});
});

it('should return an empty doc if mdip registry invalid', async () => {
const keypair = cipher.generateRandomJwk();
const agentOp = await createAgentOp(keypair, 1, 'mock');
const doc = await gatekeeper.generateDoc(agentOp);

expect(doc).toStrictEqual({});
});
});

describe('createDID', () => {
afterEach(() => {
mockFs.restore();
Expand Down
2 changes: 1 addition & 1 deletion src/keymaster-app/src/keymaster-lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ function fetchId(id) {
idInfo = wallet.ids[wallet.current];

if (!idInfo) {
throw new Error(exceptions.UNKNOWN_ID);
throw new Error(exceptions.NO_CURRENT_ID);
}
}

Expand Down

0 comments on commit 8a3833d

Please sign in to comment.