refactor: replace in-memory cache with pg-backed repository - #667
Open
silent-cipher wants to merge 7 commits into
Open
refactor: replace in-memory cache with pg-backed repository#667silent-cipher wants to merge 7 commits into
silent-cipher wants to merge 7 commits into
Conversation
silent-cipher
marked this pull request as ready for review
July 30, 2026 16:02
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 45 out of 45 changed files in this pull request and generated no new comments.
Suppressed comments (5)
apps/backend/src/wallet-sdk/wallet-sdk.service.ts:112
- Important:
ensureProvidersLoadedcan still trigger a multi-pod thundering herd on cold start. If the DB is empty, every pod will seecountByNetwork() === 0and callloadProviders()(chain RPC) concurrently, which can reintroduce the rate-limit bursts this PR is trying to eliminate. Consider adding a Postgres-backed distributed lock (e.g. advisory lock per network) around the "count==0 → load" path and re-checking the count after acquiring the lock.
apps/backend/src/piece-cleanup/piece-cleanup.service.spec.ts:457 - This assertion expects a number (
9), butdeletePiece(..., providerId)is typed asbigint | undefinedand should receive9nfrom the repository. Keeping the assertion as a number can let a regression slip through ifproviderIdever gets coerced incorrectly.
for (const call of deletePieceSpy.mock.calls) {
expect(call[5]).toBe(9);
}
apps/backend/src/providers/repositories/storage-provider.repository.ts:116
- Blocker:
findByAddressesCaseInsensitivelowercases the DB column (LOWER(address)), but it passes the inputaddressesarray through unchanged. If any input address is mixed-case, theIN (...)comparison will not match and stale-provider cleanup will silently miss rows.
where: {
network,
address: Raw((alias) => `LOWER(${alias}) IN (:...addresses)`, { addresses }),
},
apps/backend/src/providers/repositories/storage-provider.repository.ts:30
- Blocker:
findByAddressperforms a case-sensitive match onaddress, which can miss rows when callers provide checksummed/mixed-case addresses (common for EVM-style addresses). This can cause providers to appear “not found” even though they exist in Postgres, leading to skipped jobs or failed checks.
async findByAddress(address: string, network: Network): Promise<PDPProviderEx | undefined> {
const row = await this.repo.findOne({ where: { address, network } });
return row ? this.hydrateProvider(row) : undefined;
apps/backend/src/piece-cleanup/piece-cleanup.service.spec.ts:89
- The repository mock returns
id: 9(number), but production provider ids arebigint(PDPProviderEx.id). Using a number here can mask type/serialization issues and makes the new providerId-passing optimization less faithful to runtime behavior.
This issue also appears on line 455 of the same file.
function createStorageProviderRepositoryMock() {
return {
findByAddress: vi.fn().mockResolvedValue({ id: 9, name: "Test SP" }),
};
beck-8
reviewed
Aug 3, 2026
beck-8
left a comment
Collaborator
There was a problem hiding this comment.
I'm not sure about this, so I'd like someone else to confirm it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Each pod kept its own in-memory copy of the on-chain storage provider registry. Only whichever pod happened to run the scheduled refresh job got fresh data. Every other pod could keep serving stale info (wrong active/inactive/approved status) until it restarted. On top of that, three different code path independently called the chain to fill this cache, and those uncoordinated bursts were the root cause of the erpc rate-limit failures behind the "providers_refresh jobs failing" alert.
What changed
StorageProviderRepository, the single place that reads and writes provider data in postgres.WalletSdkServicenow only talks to the chain from the scheduledproviders_refreshjob; every other lookup reads postgres directly.sampled-retrieval,retreival) onto the same repository.Can we use the subgraph instead of PostgreSQL?
I hadn’t considered this approach initially, but there are a couple of concerns:
sampled_retrievalsjob.