Repository navigation
Conversation
Blockfrost read queries had no time limit except in the address module, so a query kept running on the database after the client gave up. Each request handled by a Blockfrost controller now gets a query time budget (store.extensions.blockfrost.query-timeout, default 30s, 0 disables). A jOOQ ExecuteListener sets every statement's JDBC query timeout to the time left, keeping a smaller explicit queryTimeout. Spring Data repository calls made by the request run in a read-only transaction with the same remaining time. Threads without a budget, including all indexer threads, are not affected. A cancelled query answers 504 instead of 500, recognised by SQLState 57014 as Spring maps the PostgreSQL cancel to a resource failure. If a caught timeout would still produce a 200 or 404, the response is replaced with the 504. BFCommonConfiguration is imported by the auto-configuration, so the common advices no longer depend on the pools module scanning their package. Fixes #1207
|
|
Initial automated review · Aster Hi @ducpm2303, thanks for the PR! 🙏 This is an automatic first check by an AI reviewer. It does not replace maintainer review and may be wrong. Reviewed commit: No issues found in this first check. Not tested: Tests and database scenarios were not run locally; build CI was still running when checked. |
This branch has not been deployed
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.



Fixes #1207
Problem
Apart from the address module's own 15 s
queryTimeout, no Blockfrost read query had a time limit. Controllers are synchronous, so a client that times out or disconnects does not stop its query. PostgreSQL kept running it, with its parallel workers, holding a pool connection until it finished. Clients that retry multiply the work.Measured on
main, with the client giving up after 1–2 s:/assetsand/addresses/{addr}/utxoswere still running 7.2 s after the request./assets/{unit}/addressesrequests left 15 backends (8 + parallel workers) running 1.3 s later.Change
Per-request query budget. Each request handled by a Blockfrost controller gets a budget:
store.extensions.blockfrost.query-timeout, default30s(0disables it). Every statement gets the time left in the budget as its JDBC query timeout, so the request is bounded as a whole, not per statement.BF*StorageReaderImpl): anExecuteListenerregistered through anExecuteListenerProviderbean sets the timeout. A smaller explicitqueryTimeout(..), such as address's 15 s, is kept. No reader changed.TransactionStorageReader,EpochStakeStorageReader, epoch services, …): each call runs in a read-only transaction whose timeout is the remaining budget, and Hibernate applies it to the statement. This is added as an interceptor on the repository proxy, just before its ownTransactionInterceptor.ThreadLocalset by aHandlerInterceptor, only for handlers incom.bloxbean.cardano.yaci.store.blockfrost. Indexer threads never have one, so sync, rollback and the other APIs are unaffected.Response.
504 {"error":"Gateway Timeout","message":"The query took too long to complete.","status_code":504}. Other DB errors keep the 500 from fix(blockfrost): return 500 instead of empty total on query failure #1193.57014(PostgreSQL, H2) and MySQL 1317/3024, as well as byQueryTimeoutException. Spring translates the PostgreSQL cancel toDataAccessResourceFailureException, not toQueryTimeoutException.ResponseBodyAdvicereplaces the response with the 504. No catch block is touched.Wiring.
BFCommonConfigurationis now imported byBFAutoConfiguration. Before this, packageblockfrost.common, which holds the error advices, was only scanned throughBFPoolsConfiguration, so disabling the pools module silently removed them.Docs.
blockfrost/configuration: the new property, the 504, and an optional server-sidestatement_timeoutfor read-only instances. That setup needs a separate Flyway connection, because aSETinspring.flyway.init-sqlsstays on the pooled connections.Known limit. JDBC timeouts are whole seconds, so a request can overrun its budget by up to about 1 s.
Tests
Unit tests (
blockfrost-common, JUnit 5 + AssertJ):BFQueryTimeoutExecuteListenerTest(H2):BFRepositoryTimeoutInterceptorTest: read-only transaction with the remaining time; pass-through without a budget; placed before theTransactionInterceptor.BFQueryTimeoutWebTest: budget only for Blockfrost handlers, cleared after the request; a swallowed timeout becomes 504 instead of an empty 200 or a 404.BFExceptionHandlerTest:QueryTimeoutException→ 504; PostgreSQL57014translated as a resource failure → 504; other DB errors → 500.BFQueryTimeoutsTest,BFQueryTimeoutContextTest.End to end on preprod and preview. Each DB was served by
main@a65a828a8(before) and this branch (after), with a small budget so the local data is slow enough./assets,/assets/{u}/addresses,/assets/{u}/txs,/addresses/{a}/utxos,/addresses/{a}/total)canceling statement due to user request/txs/{h}/cborbehind an 8 s table lock/,/blocks/{n},/blocks/{n}/txs,/txs/{h},/txs/{h}/utxos,/txs/{h}/metadata,/assets/{u},/scripts/{h},/metadata/txs/labels/{l})confirmations)confirmations)Also checked on preprod:
query-timeout=0, the address module's own 15 s timeout on/addresses/{a}/utxosnow answers 504 (500 onmain).statement_timeout=2sin the URL, separate Flyway connection) starts normally and answers 504, with PostgreSQL loggingcanceling statement due to statement timeout.