Add generic query-catalog and source-worker publishing flow#326
Add generic query-catalog and source-worker publishing flow#326
Conversation
f9975ae to
b45a1de
Compare
| await this.ensureGraph(); | ||
| const next = this.refreshActiveLease(this.mergeJob(await this.getRequiredJob(jobId), status, data)); | ||
| const current = await this.getRequiredJob(jobId); | ||
| await this.assertActiveClaimFence(current); |
There was a problem hiding this comment.
🔴 Bug: This only fences the update() path. A worker that loses its claim while publishExecutor is running can still mutate the job through recordPublishResult() / recordPublishFailure(), because those methods still write state without re-checking the wallet lock / claim token. Please apply the same fence validation before every post-claim mutation and add regression coverage for the publish-result/failure paths.
| continue; | ||
| } | ||
|
|
||
| const nextAttemptCount = current?.fingerprint === fingerprint ? (current.attemptCount ?? 0) + 1 : 1; |
There was a problem hiding this comment.
🔴 Bug: no-matching-rows is treated as a success state, but this loop only derives success from lastJobIds. If a handler returns { lastStatus: 'no-matching-rows' } with no jobs, the next poll sees aggregate === '', falls through here, and keeps retrying until the source is incorrectly forced into manual review. Short-circuit same-fingerprint no-matching-rows before incrementing retries (or persist a synthetic terminal status into the aggregate calculation).
| provider, | ||
| ); | ||
| const tokenAddr = await hub.getContractAddress("Token").catch(() => null); | ||
| const tokenAddr = config.chain?.tokenAddress |
There was a problem hiding this comment.
🔴 Bug: This reads only config.chain?.tokenAddress, so a token override coming from the merged network config is ignored here. In that case /api/wallets/balances still falls back to Hub.Token, which can make the UI show the wrong token or fail entirely while dkg wallet succeeds. Use the resolved chain/network token address in this route too.
b45a1de to
bfe8ad9
Compare
|
|
||
| try { | ||
| const result = await deps.processSource(source, fingerprint, current); | ||
| nextState.sources[source.id] = result.nextState; |
There was a problem hiding this comment.
🔴 Bug: processSource() gets the computed fingerprint, but the worker persists result.nextState verbatim. If a handler omits that field from nextState (which is easy with the current API), the next poll sees current.fingerprint as missing and reprocesses the same source again, potentially creating duplicate share/publish jobs forever. Merge the framework-owned fields here (fingerprint, lastRunAt, and any retry/manual-review resets) instead of requiring every handler to remember them.
| { subject: queryUri, predicate: `${PROFILE_NS}inCatalog`, object: catalogUri, graph: '' }, | ||
| { subject: queryUri, predicate: `${PROFILE_NS}displayName`, object: literal(name), graph: '' }, | ||
| { subject: queryUri, predicate: `${PROFILE_NS}sparqlQuery`, object: literal(sparql), graph: '' }, | ||
| { subject: queryUri, predicate: `${PROFILE_NS}rank`, object: intLiteral(rank), graph: '' }, |
There was a problem hiding this comment.
🔴 Bug: rank comes from Date.now() above, but this writes it as xsd:int. Millisecond timestamps are already around 1.7e12, far beyond the 32-bit range of xsd:int, so the stored literal is invalid/overflow-prone and query ordering can become store-dependent. Persist this as xsd:integer/xsd:long, or use a smaller bounded rank value.
| const [saveMessage, setSaveMessage] = useState<string | null>(null); | ||
| const builtInCatalog = useMemo(() => contextGraphBuiltInCatalog(contextGraphId), [contextGraphId]); | ||
| const queryCatalogs = useMemo( | ||
| () => [builtInCatalog, ...localSavedCatalogs, ...(profile?.queryCatalogs ?? [])], |
There was a problem hiding this comment.
🟡 Issue: This concatenates localSavedCatalogs and profile.queryCatalogs without merging by catalog identity. If the persisted profile already contains ui-saved-queries and the user saves another query, the UI renders duplicate catalog sections with the same React key and splits the queries across them. Merge catalogs by subGraph|slug before rendering.
Summary
CLI Usage
The new source-worker entrypoint runs a generic ingestion handler from a config file and pushes work through the existing async publish flow.
Run continuously:
Run once for cron/manual execution:
The worker config points the CLI at:
This keeps the worker runtime generic in dkg, while allowing source-specific adapters to live outside the core repo.