Skip to content

Cache fast-path quote solutions in the driver - #4678

Open
AryanGodara wants to merge 4 commits into
mainfrom
aryan/be-58-cache-quote-solutions
Open

Cache fast-path quote solutions in the driver#4678
AryanGodara wants to merge 4 commits into
mainfrom
aryan/be-58-cache-quote-solutions

Conversation

@AryanGodara

Copy link
Copy Markdown
Member

Description

Fast-path (out-of-competition) execution reuses a quote's solution to settle the matching order during an exclusivity window instead of running a full auction. For that the driver must keep the solution it computes at quote time (rn /quote discards it). Now we cache the fast-path quote solution in the driver, keyed by (auction_id, solution_id), and usesthose ids on the quote response so a later settle can find it.

Why cache the solution and not a ready Settlement: a quote runs against an unsigned order, so a submittable settlement can't be encoded/simulated until the real signed order exists at settle time. (tried this, goes beyond scope of the PR 🤔 )

Changes

  • add a quote_solutions cache to Competition (CachedQuoteSolution { auction, solution }) + cache_quote_solution(): no simulation, bounded like the settlements deque.
  • /quote caches the solution when the quote is fast-path and the request carries an auctionId, and echoes solutionId + auctionId on the response.
  • /quote request accepts auctionId; the handler now passes the whole Competition to Order::quote (was just &risk_detector) so it can reach the cache — the risk-detector filtering is unchanged.
  • add openApi docs and fix the request flag name fastPathenableFastPath to match the code.

Out of scope / follow-ups:

  • Orderbook allocating auctionId from the shared auctions sequence and sending it in the /quote request (BE-56) — not included; the driver accepts the id but nothing supplies one yet
  • Fast-path settle path: read the cache, re-encode against the real signed order, submit (BE-57 / quote-reuse fork)
  • Observer/accounting for fast-path settlements (BE-61)

Related Issues

BE-58

Follow-ups: BE-56, BE-57, BE-61.

@AryanGodara AryanGodara self-assigned this Jul 28, 2026
@linear-code

linear-code Bot commented Jul 28, 2026

Copy link
Copy Markdown

BE-58

@github-actions

Copy link
Copy Markdown

Reminder: Please consider backward compatibility when modifying the API specification.
If breaking changes are unavoidable, ensure:

  • You explicitly pointed out breaking changes.
  • You communicate the changes to affected teams (at least Frontend team and SAFE team).
  • You provide proper versioning and migration mechanisms.

Caused by:

@AryanGodara
AryanGodara marked this pull request as ready for review July 28, 2026 12:45
@AryanGodara
AryanGodara requested a review from a team as a code owner July 28, 2026 12:45
@claude

claude Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Claude finished @AryanGodara's task in 2m 7s —— View job


PR Review: Cache fast-path quote solutions in the driver

  • Read changed files & understand the change
  • Review domain logic (competition cache, quote.rs)
  • Review API DTOs & openapi
  • Review tests

Overall this is a clean, well-scoped change and the code reads well. Caching the solution (not a Settlement) is the right call given the unsigned quote order, the eviction/locking mirrors the existing settlements deque, the API renaming fastPathenableFastPath is a doc fix (not a real break), and the request/response DTOs, openapi, and tests all line up consistently.

One point worth revisiting — posted inline:

  • Cache eviction bound is mis-sized for quotes (competition/mod.rs:558): reusing max_solutions_to_propose() * MAX_CONCURRENT_AUCTIONS (=5) from the settlements cache doesn't map to quote cadence; a cached fast-path solution could be evicted before its /settle arrives in the exclusivity window. Non-blocking since the settle path (BE-57) isn't wired up yet, but worth sizing against expected fast-path quote volume (or a time-based expiry) before it lands.

Nothing else stood out — the fast-path/auction_id gating in quote.rs, the Quote::try_new borrow change, and the serde skip_serializing_if on the new response fields are all correct.

Comment thread crates/driver/src/domain/competition/mod.rs Outdated

@MartinquaXD MartinquaXD left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you already check how those quotes could be turned into submittable transactions? That could inform us which additional API changes are needed.

Comment on lines +67 to +69
/// Upper bound on cached fast-path quote solutions, each fast-path quote pushes
/// exactly one entry, at a cadence unrelated to auctions.
const MAX_CACHED_QUOTE_SOLUTIONS: usize = 100;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if we want to give solvers optimal information for risk/inventory management we should probably tell them an expiration for the quote. This can then be used for the cache eviction.
Not necessary in the very first step, I think.

/// throwaway auction it was solved against.
#[derive(Debug)]
pub struct CachedQuoteSolution {
pub auction: Auction,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we storing the full auction here? Since we don't compute any new data we only need to have the solution be associated with the auction_id, no?

Ok(scored.into_iter().map(|(solved, _)| solved).collect())
}

/// Caches a fast-path quote's solution, keyed by the auction id (allocated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the solution is not really keyed by anything since we are not storing it in a map to uphold uniqueness.
That being said using a map makes more sense here since those solutions will on average not be used in order.

Comment on lines +161 to +163
// For fast-path quotes the orderbook allocates a real auction id from the
// shared `auctions` sequence and forwards it here, so the solution can be
// encoded into a settlement and cached for a later `/settle`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The driver should not make any assumptions on how the id gets generated. The only important invariant is that there will be a unique auction_id for each fast path quote.

Comment on lines +38 to +42
/// For fast-path quotes: the id of the cached solution and the auction id
/// it was cached under, so the caller can later settle it via `/settle`.
/// `None` for regular quotes.
pub solution_id: Option<u64>,
pub auction_id: Option<i64>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like this should actually not be part of the quote but rather be in the key the quote gets stored in a collection with.

if self.enable_fast_path && !solver.fast_path_enabled() {
return Err(Error::QuotingFailed(QuotingFailed::FastPathNotSupported));
}
let fast_path = self.enable_fast_path && solver.fast_path_enabled();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this point we should stop looking at solver.fast_path_enabled(). We already checked above that fast path support is enabled.

.map(auction::Id);
let auction = self
.fake_auction(eth, tokens, solver.quote_using_limit_orders())
.fake_auction(eth, tokens, solver.quote_using_limit_orders(), auction_id)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have to pass auction_id if self already contains it?

Comment on lines +194 to +195
quote.solution_id = Some(solution_id);
quote.auction_id = Some(auction_id.0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems wrong to add this data to the quote itself. Unless I overlook something I'd go with HashMap<(auction_id, solution_id), Quote> or something like that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants