From 612923b55c260eae58350dafa751d93171c754e9 Mon Sep 17 00:00:00 2001 From: DarkSky <25152247+darkskygit@users.noreply.github.com> Date: Wed, 26 Aug 2026 22:14:17 +0800 Subject: [PATCH] fix(server): indexer feature (#15533) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description ## Checklist - [ ] I have signed the [AFFiNE Contributor License Agreement](https://cla-assistant.io/toeverything/AFFiNE) — required before merge; the `license/cla` check must be green ([how it works](https://github.com/toeverything/AFFiNE/blob/canary/docs/BUILDING.md#sign-the-cla-first)) - [ ] The PR targets the `canary` branch and its title follows [Conventional Commits](https://www.conventionalcommits.org/) - [ ] Tests are added or updated where it makes sense - [ ] `yarn lint` and `yarn typecheck` pass locally #### PR Dependency Tree * **PR #15533** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) ## Summary by CodeRabbit * **Bug Fixes** * Improved indexer feature synchronization when the indexer is enabled or configuration changes. * Enabled indexer-related capabilities without waiting for native search readiness checks. * Improved consistency across search, aggregate, document, and application startup flows. --- .../src/__tests__/e2e/config/resolver.spec.ts | 27 +++++++------------ .../plugins/indexer/__tests__/service.spec.ts | 11 +++----- .../server/src/plugins/indexer/service.ts | 20 +++++++------- 3 files changed, 22 insertions(+), 36 deletions(-) diff --git a/packages/backend/server/src/__tests__/e2e/config/resolver.spec.ts b/packages/backend/server/src/__tests__/e2e/config/resolver.spec.ts index af1f730070..cf25d75569 100644 --- a/packages/backend/server/src/__tests__/e2e/config/resolver.spec.ts +++ b/packages/backend/server/src/__tests__/e2e/config/resolver.spec.ts @@ -1,27 +1,18 @@ import { serverConfigQuery, ServerFeature } from '@affine/graphql'; import { Config } from '../../../base'; -import { BackendRuntimeProvider } from '../../../core/backend-runtime'; -import { IndexerService } from '../../../plugins/indexer'; import { app, e2e } from '../test'; -e2e( - 'should expose the indexer feature when its projection is ready', - async t => { - const enabled = app.get(Config).indexer.enabled; - if (enabled) { - await app.get(BackendRuntimeProvider).reconcileSearchProjection(1000); - await app.get(IndexerService).onApplicationBootstrap(); - } - const { serverConfig } = await app.gql({ query: serverConfigQuery }); +e2e('should expose the indexer feature when it is enabled', async t => { + const enabled = app.get(Config).indexer.enabled; + const { serverConfig } = await app.gql({ query: serverConfigQuery }); - t.is( - serverConfig.features.includes(ServerFeature.Indexer), - enabled, - JSON.stringify(serverConfig, null, 2) - ); - } -); + t.is( + serverConfig.features.includes(ServerFeature.Indexer), + enabled, + JSON.stringify(serverConfig, null, 2) + ); +}); e2e('should comment feature enabled by default', async t => { const { serverConfig } = await app.gql({ query: serverConfigQuery }); diff --git a/packages/backend/server/src/plugins/indexer/__tests__/service.spec.ts b/packages/backend/server/src/plugins/indexer/__tests__/service.spec.ts index 25eac19213..c07d115370 100644 --- a/packages/backend/server/src/plugins/indexer/__tests__/service.spec.ts +++ b/packages/backend/server/src/plugins/indexer/__tests__/service.spec.ts @@ -31,7 +31,7 @@ function enabledServer() { }; } -test('reflects native search readiness in the Node feature flag', async t => { +test('exposes the indexer capability while its projection is building', async t => { const runtime = { searchStatus: Sinon.stub(), searchAuthorized: Sinon.stub().resolves({ @@ -39,9 +39,6 @@ test('reflects native search readiness in the Node feature flag', async t => { value: { total: 0, nodes: [] }, }), }; - runtime.searchStatus.onFirstCall().resolves({ ready: true }); - runtime.searchStatus.onSecondCall().resolves({ ready: false }); - runtime.searchStatus.onThirdCall().resolves({ ready: true }); const server = enabledServer(); const service = new IndexerService( runtime as unknown as BackendRuntimeProvider, @@ -53,9 +50,9 @@ test('reflects native search readiness in the Node feature flag', async t => { await service.onConfigChanged({ updates: { indexer: {} } } as never); await service.search('actor', 'workspace', {} as never); - t.is(server.enableFeature.callCount, 2); - t.true(server.disableFeature.calledOnce); - t.is(runtime.searchStatus.callCount, 3); + t.is(server.enableFeature.callCount, 3); + t.false(server.disableFeature.called); + t.false(runtime.searchStatus.called); }); test('does not query native search when the indexer is disabled', async t => { diff --git a/packages/backend/server/src/plugins/indexer/service.ts b/packages/backend/server/src/plugins/indexer/service.ts index 8fd84eee10..75f7eac40e 100644 --- a/packages/backend/server/src/plugins/indexer/service.ts +++ b/packages/backend/server/src/plugins/indexer/service.ts @@ -45,26 +45,24 @@ export class IndexerService implements OnApplicationBootstrap { ) {} async onApplicationBootstrap() { - await this.syncFeature(); + this.syncFeature(); } @OnEvent('config.changed.broadcast') async onConfigChanged({ updates }: Events['config.changed.broadcast']) { - if (updates.indexer) await this.syncFeature(); + if (updates.indexer) this.syncFeature(); } - private async syncFeature() { - if (!this.server.getConfig().indexer.enabled) { + private syncFeature() { + if (this.server.getConfig().indexer.enabled) { + this.server.enableFeature(ServerFeature.Indexer); + } else { this.server.disableFeature(ServerFeature.Indexer); - return; } - const status = (await this.runtime.searchStatus()) as { ready: boolean }; - if (status.ready) this.server.enableFeature(ServerFeature.Indexer); - else this.server.disableFeature(ServerFeature.Indexer); } async search(actorUserId: string, workspaceId: string, input: SearchInput) { - await this.syncFeature(); + this.syncFeature(); const result = this.unwrap( await this.runtime.searchAuthorized(actorUserId, workspaceId, input), workspaceId @@ -77,7 +75,7 @@ export class IndexerService implements OnApplicationBootstrap { workspaceId: string, input: AggregateInput ) { - await this.syncFeature(); + this.syncFeature(); const result = this.unwrap( await this.runtime.aggregateAuthorized(actorUserId, workspaceId, input), workspaceId @@ -104,7 +102,7 @@ export class IndexerService implements OnApplicationBootstrap { keyword: string, options?: { limit?: number; docIds?: string[] } ): Promise { - await this.syncFeature(); + this.syncFeature(); if (options?.limit !== undefined && options.limit <= 0) { throw new InvalidIndexerInput({ reason: 'searchDocs limit must be positive',