fix(asset): write decimals through the service that owns the cache - #4584
Merged
Conversation
The hourly decimals job wrote through the repository instance built by RepositoryFactory, while every reader goes through AssetService and its own instance. invalidateCache() only clears the instance it is called on, so the freshly written decimals stayed invisible to readers until the entry expired. The write now goes through AssetService, which invalidates the cache the readers are served from. updatePrices did exactly that already and was the only caller of its kind, so it is renamed to updateAssets and shared instead of copied. The job also collects its updates and writes once: it runs hourly and usually finds nothing, so invalidating unconditionally would drop the whole asset cache every hour for no reason.
Batching the writes had given up the failure isolation the per-asset loop used to have: if one update threw, invalidateCache() was never reached and the rows written before it stayed out of the cache — the exact staleness this change exists to remove. The invalidation now sits in a finally block, so it also runs when an update fails. The empty case is guarded in the service instead of at the call site, which makes it hold for every caller: the price job passed an empty list every five minutes and dropped the whole asset cache for nothing.
…lure The finally block covered the cache but not the loop: the first failing update still aborted the batch, so every asset queued behind it was skipped. Before this change each asset was written on its own, and a row that keeps failing was harmless — as a batch it would block all following assets on every run. Every update is attempted now, failures are collected, the cache is invalidated either way, and the collected ids are reported afterwards.
Collecting the failures had reduced them to text: the caller lost the type, the stack and the code of the database error, and every cause after the first was dropped. Whoever reads the log needs the error itself. AggregateError carries all of them and names the affected ids in its message. No dedicated error class with a failedIds field: neither caller inspects the error, both hand it to the cron, so that would be structure without a consumer.
Collaborator
Author
|
Four review passes, three defects fixed. Each one was a hole left by the previous version of the same method, so they are worth naming:
The fourth pass found nothing. One recommendation was deliberately not followed: a dedicated error type exposing CI green: build and checks, all three test shards, coverage, and the coverage ratchet. |
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.
Closes #4578.
The defect
EvmDecimalsService.setDecimals()wrote throughrepoFactory.asset, the instanceRepositoryFactoryconstructs itself. Every reader goes throughAssetService, which holds a different instance of the same repository class.CachedRepository.invalidateCache()clears only the instance it is called on, so nothing told the readers' cache thatdecimalshad just been filled — it kept serving the previous rows until the entry expired.The impact was small: the job only fills rows that are still
NULL, so it delayed a repair rather than serving a wrong value. The exposure belongs to the pattern, not to this field.The fix
The write goes through
AssetService, which owns the instance the cached reads are served from.AssetService.updatePricesdid precisely that already and had exactly one caller, so instead of copying its body it is renamed toupdateAssetsand used by both jobs. Reading moves behind the service as well (getEvmAssetsWithoutDecimals), soRepositoryFactory.assetis left without a single caller.updateAssetsthen took three corrections during review, each one closing a hole the previous version had left:finally. The first version invalidated after the loop, so a failing update skipped it and left the rows written before it out of the cache — the very staleness this change removes.AggregateErrorcarrying all of them and naming the affected ids.On (3) a dedicated error class exposing
failedIdswas considered and rejected: neither caller inspects the error, both hand it to the cron, so it would be structure without a consumer.The empty case is guarded inside the service rather than at the call site, so it holds for every caller. That also changes the five-minute price job, which previously passed an empty list and dropped the whole asset cache for nothing.
Tests
asset.service.update.spec.ts(new)evm-decimals.service.spec.ts(new)Reverting the write turns three of the decimals tests red.
Not covered here
The pattern itself — a repository existing twice, in DI and in
RepositoryFactory, with independent caches — stays as it is; nothing enforces the service boundary, so a future direct repository write could recreate this. Issue #4578 records the structural options.Checks
npm run type-check,npm run lintandprettier --checkclean; the new specs plus theasset.serviceandasset-prices-jobsuites green (13 tests).