fix(api): fetch relay swaps first, and never idle while the queue has work
ci / gate (push) Successful in 4m32s
deploy / validate (push) Skipped
deploy / build (push) Successful in 9m35s
deploy / deploy-api (push) Successful in 42s
deploy / deploy-web (push) Successful in 10s

After launch the Ref fetch loop slept ten seconds between batches of
twenty, oldest first: 1,574 relay swaps — real 1Click trades through the
pool — waited behind thousands of an arbitrage bot's direct calls, which
the re-read had queued and which mostly fail, draining at about six a
minute. The page's pool swaps and price chart ran hours behind.

The queue now serves the relay's entries first, then the least-retried,
newest first, and loops straight on while batches (now fifty) come back
full; FastNEAR's pacing is the only limit, about a hundred a minute.

Refs #37, #39

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015pkwBsR54NRmZyU4QVhvfj
This commit is contained in:
2026-10-06 19:24:36 +03:00
co-authored by Claude Opus 5.5
parent b3dc4ef4f6
commit 6988368d6e
3 changed files with 24 additions and 7 deletions
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n select tx_hash, signer, near_height, trader, attempts from ref_pending\n where chain = $1 and near_height <= $2\n order by near_height limit $3\n ",
"query": "\n select tx_hash, signer, near_height, trader, attempts from ref_pending\n where chain = $1 and near_height <= $2\n -- The relay's first: each is a solver's swap through the pool, near\n -- certain, where a direct call is as often a bot's failed attempt.\n -- Then the least-retried, newest first, so the page stays current\n -- and a backlog fills in behind it rather than ahead of it.\n order by (signer = 'intents.near') desc, attempts, near_height desc\n limit $3\n ",
"describe": {
"columns": [
{
@@ -44,5 +44,5 @@
false
]
},
"hash": "3de8c1993770595fd108c10eeba83f0c0107e0aa24dfd440ac3841320efb6615"
"hash": "27db80d44ed3461395ff90a0fc77e443354f720243e45d1a6c62ed466b49696f"
}
+16 -4
View File
@@ -42,7 +42,10 @@ pub const READS: &str = "Ref DCL pools holding the asset — liquidity and limit
/// enough for the pool's receipt, which runs a block or two after the relay's.
const FETCH_AFTER_BLOCKS: u64 = 20;
/// How often the queue is worked.
/// Queued transactions taken per pass.
const FETCH_BATCH: i64 = 50;
/// How often the queue is worked when it is empty.
const FETCH_INTERVAL: Duration = Duration::from_secs(10);
/// Attempts before a queued transaction is given up on.
@@ -100,8 +103,14 @@ async fn fetch(chain: Arc<ChainRuntime>, store: Store, near: NearClient, config:
let Some(asset_contract) = config.token_contract().map(str::to_owned) else {
return;
};
let mut backlog = false;
loop {
tokio::time::sleep(FETCH_INTERVAL).await;
// Straight on while the last batch was full: the wait is for an empty
// queue, never for one with work in it. After launch it slept ten
// seconds between batches of twenty while 1,574 relay swaps waited.
if !backlog {
tokio::time::sleep(FETCH_INTERVAL).await;
}
let tip = match near.heights().await {
Ok((tip, _)) => tip,
Err(e) => {
@@ -110,10 +119,13 @@ async fn fetch(chain: Arc<ChainRuntime>, store: Store, near: NearClient, config:
}
};
let due = match store
.due_ref_pending(&id, tip.saturating_sub(FETCH_AFTER_BLOCKS), 20)
.due_ref_pending(&id, tip.saturating_sub(FETCH_AFTER_BLOCKS), FETCH_BATCH)
.await
{
Ok(d) => d,
Ok(d) => {
backlog = d.len() as i64 == FETCH_BATCH;
d
}
Err(e) => {
tracing::warn!(chain = %id, error = %e, "ref queue unreadable");
continue;
+6 -1
View File
@@ -3334,7 +3334,12 @@ impl Store {
r#"
select tx_hash, signer, near_height, trader, attempts from ref_pending
where chain = $1 and near_height <= $2
order by near_height limit $3
-- The relay's first: each is a solver's swap through the pool, near
-- certain, where a direct call is as often a bot's failed attempt.
-- Then the least-retried, newest first, so the page stays current
-- and a backlog fills in behind it rather than ahead of it.
order by (signer = 'intents.near') desc, attempts, near_height desc
limit $3
"#,
chain.as_str(),
height as i64,