Skip to content

Commit faecad2

Browse files
gmaclennanachou11
andauthored
fix: wait for index idle before returning data (#389)
* WIP initial work * rename Rpc to LocalPeers * Handle deviceInfo internally, id -> deviceId * Tests for stream error handling * remove unnecessary constructor * return replication stream * Attach protomux instance to peer info * rename and re-organize * revert changes outside scope of PR * WIP initial work * Tie everything together * rename getProjectInstance * feat: client.listLocalPeers() & `local-peers` evt * feat: add $sync API methods For now this simplifies the API (because we are only supporting local sync, not remote sync over the internet) to: - `project.$sync.getState()` - `project.$sync.start()` - `project.$sync.stop()` - Events - `sync-state` It's currently not possible to stop local discovery, nor is it possible to stop sync of the metadata namespaces (auth, config, blobIndex). The start and stop methods stop the sync of the data and blob namespaces. Fixes #134. Stacked on #360, #358 and #356. * feat: Add project.$waitForInitialSync() method Fixes Add project method to download auth + config cores #233 Rather than call this inside the `client.addProject()` method, instead I think it is better for the API consumer to call `project.$waitForInitialSync()` after adding a project, since this allows the implementer to give user feedback about what is happening. * Wait for initial sync within addProject() * fix: don't add core bitfield until core is ready * feat: expose deviceId on coreManager * fix: wait for project.ready() in waitForInitialSync * fix: skip waitForSync in tests * don't enable/disable namespace if not needed * start core download when created via sparse: false * Add debug logging This was a big lift, but necessary to be able to debug sync issues since temporarily adding console.log statements was too much work, and debugging requires knowing the deviceId associated with each message. * fix timeout * fix: Add new cores to the indexer (!!!) This caused a day of work: a bug from months back * remove unnecessary log stmt * get capabilities.getMany() to include creator * fix invite test * keep blob cores sparse * optional param for LocalPeers * re-org sync and replication Removes old replication code attached to CoreManager Still needs tests to be updated * update package-lock * chore: Add debug logging * Add new logger to discovery + dnssd * Get invite test working * fix manager logger * cleanup invite test (and make it fail :( * fix: handle duplicate connections to LocalPeers * fix stream close before channel open * send invite to non-existent peer * fixed fake timers implementation for tests * new tests for duplicate connections * cleanup and small fix * Better state debug logging * chain of invites test * fix max listeners and add skipped test * fix: only request a core key from one peer Reduces the number of duplicate requests for the same keys. * cleanup members tests with new helprs * wait for project ready when adding * only create 4 clients for chain of invites test * add e2e sync tests * add published @mapeo/mock-data * fix: don't open cores in sparse mode Turns out this changes how core.length etc. work, which confuses things * fix: option to skip auto download for tests * e2e test for stop-start sync * fix coreManager unit tests * fix blob store tests * fix discovery-key event * add coreCount to sync state * test sync with blocked peer & fix bugs * fix datatype unit tests * fix blobs server unit tests * remote peer-sync-controller unit test This is now tested in e2e tests * fix type issues caused by bad lockfile * ignore debug type errors * fixes for review comments * move utils-new into utils * Add debug info to test that sometimes fails * Update package-lock.json version * remove project.ready() (breaks things) * wait for coreOwnership write before returning * use file storage in tests (breaks things) * Catch race condition in CRUD tests * fix race condition with parallel writes * fix tests for new createManagers syntax * fix flakey test This test relied on `peer.connectedAt` changing in order to distinguish connections, but sometimes `connectedAt` was the same for both peers. This adds a 1ms delay before making the second connection, to attempt to stop the flakiness. * fix: wait for index idle before returning data * temp fixes to run CI * small fix for failing test * update to published multi-core-indexer --------- Co-authored-by: Andrew Chou <[email protected]>
1 parent 33d0f20 commit faecad2

File tree

9 files changed

+26
-16
lines changed

9 files changed

+26
-16
lines changed

package-lock.json

Lines changed: 4 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@
129129
"hyperswarm": "4.4.1",
130130
"magic-bytes.js": "^1.0.14",
131131
"map-obj": "^5.0.2",
132-
"multi-core-indexer": "1.0.0-alpha.7",
132+
"multi-core-indexer": "^1.0.0-alpha.9",
133133
"p-defer": "^4.0.0",
134134
"p-timeout": "^6.1.2",
135135
"patch-package": "^8.0.0",

src/core-ownership.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export class CoreOwnership {
6565
expressions.push(eq(table[`${namespace}CoreId`], coreId))
6666
}
6767
// prettier-ignore
68-
const result = this.#dataType[kSelect]()
68+
const result = (await this.#dataType[kSelect]())
6969
.where(or.apply(null, expressions))
7070
.get()
7171
if (!result) {

src/datastore/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ export class DataStore extends TypedEmitter {
8484
})
8585
}
8686

87+
get indexer() {
88+
return this.#coreIndexer
89+
}
90+
8791
get namespace() {
8892
return this.#namespace
8993
}

src/datatype/index.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ export class DataType {
140140
* @param {string} docId
141141
*/
142142
async getByDocId(docId) {
143+
await this.#dataStore.indexer.idle()
143144
const result = this.#sql.getByDocId.get({ docId })
144145
if (!result) throw new Error('Not found')
145146
return deNullify(result)
@@ -151,6 +152,7 @@ export class DataType {
151152
}
152153

153154
async getMany() {
155+
await this.#dataStore.indexer.idle()
154156
return this.#sql.getMany.all().map((doc) => deNullify(doc))
155157
}
156158

@@ -161,6 +163,7 @@ export class DataType {
161163
* @param {T} value
162164
*/
163165
async update(versionId, value) {
166+
await this.#dataStore.indexer.idle()
164167
const links = Array.isArray(versionId) ? versionId : [versionId]
165168
const { docId, createdAt, createdBy } = await this.#validateLinks(links)
166169
/** @type {any} */
@@ -181,6 +184,7 @@ export class DataType {
181184
* @param {string | string[]} versionId
182185
*/
183186
async delete(versionId) {
187+
await this.#dataStore.indexer.idle()
184188
const links = Array.isArray(versionId) ? versionId : [versionId]
185189
const { docId, createdAt, createdBy } = await this.#validateLinks(links)
186190
/** @type {any} */
@@ -200,7 +204,8 @@ export class DataType {
200204
/**
201205
* @param {Parameters<import('drizzle-orm/better-sqlite3').BetterSQLite3Database['select']>[0]} fields
202206
*/
203-
[kSelect](fields) {
207+
async [kSelect](fields) {
208+
await this.#dataStore.indexer.idle()
204209
return this.#db.select(fields).from(this.#table)
205210
}
206211

test-e2e/manager-basic.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,9 @@ test('Consistent storage folders', async (t) => {
280280
},
281281
{ waitForSync: false }
282282
)
283-
await manager.getProject(projectId)
283+
const project = await manager.getProject(projectId)
284+
// awaiting this ensures that indexing is done, which means that indexer storage is created
285+
await project.$getOwnCapabilities()
284286
}
285287

286288
// @ts-ignore snapshot() is missing from typedefs

test-e2e/sync.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import { BLOCKED_ROLE_ID, COORDINATOR_ROLE_ID } from '../src/capabilities.js'
1818

1919
const SCHEMAS_INITIAL_SYNC = ['preset', 'field']
2020

21-
test('Create and sync data', async function (t) {
22-
const COUNT = 5
21+
test('Create and sync data', { timeout: 100_000 }, async function (t) {
22+
const COUNT = 10
2323
const managers = await createManagers(COUNT, t)
2424
const [invitor, ...invitees] = managers
2525
const disconnect = connectPeers(managers, { discovery: false })
@@ -252,5 +252,9 @@ test('no sync capabilities === no namespaces sync apart from auth', async (t) =>
252252
t.alike(invitorState[ns].localState, inviteeState[ns].localState)
253253
}
254254

255+
// Temp fix until we have .close() method - waits for indexing idle to ensure
256+
// we don't close storage in teardown while index is still being written.
257+
await Promise.all(projects.map((p) => p.$getProjectSettings()))
258+
255259
await disconnect1()
256260
})

test-e2e/utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { randomInt } from 'node:crypto'
1212
import { temporaryDirectory } from 'tempy'
1313
import fsPromises from 'node:fs/promises'
1414

15-
const FAST_TESTS = true
15+
const FAST_TESTS = !!process.env.FAST_TESTS
1616
const projectMigrationsFolder = new URL('../drizzle/project', import.meta.url)
1717
.pathname
1818
const clientMigrationsFolder = new URL('../drizzle/client', import.meta.url)

tests/datastore.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,6 @@ test('index events', async (t) => {
100100
await dataStore.write(obs)
101101
await idlePromise
102102
const expectedStates = [
103-
{
104-
current: 'idle',
105-
remaining: 0,
106-
},
107103
{
108104
current: 'indexing',
109105
remaining: 1,

0 commit comments

Comments
 (0)