a0b32b4e - Order the Safe account history by the timestamp it shows - #4461
Conversation
The list displays completedAt for a completed order and created for every other, but was ordered by created alone. An order recorded later and settled faster therefore sat below an older row carrying a younger date. Sorting by the displayed timestamp also fixes which 100 rows survive the cap - previously the cut was made against a different key than the one the reader sees.
take() splits the query into an id pass and an entity pass, and parses the raw orderBy string at every dot on the way - the COALESCE expression turns into a lookup for an alias that does not exist, and the call throws before any SQL reaches the database. Every relation joined here is to-one, so no row can be duplicated and limiting rows equals limiting entities.
|
Two review passes so far. The first found a blocking defect in the original version of this change and it is worth recording, because it would not have surfaced in any unit test.
Verified end to end against a running local instance with real data, not only by reading:
|
Two orders can carry the same valuta. Without a tiebreaker their relative order is whatever the database happens to return, so rows could swap places between calls, or cross the cap and disappear from the list entirely.
|
Three review passes. Two real defects came out of them, neither of which a unit test would have caught:
Verified against a running instance after each change, not only by reading — the list loads, an order recorded 28.05. and settled 14.06. sorts first where it previously sorted last, and the generated SQL was inspected to confirm the alias translation and that ordering precedes the cap. |
Follow-up to #4448 and DFXswiss/services#1207, both merged.
The problem
The activity list now shows a timestamp per entry: the valuta timestamp for a completed order, the creation date for every other state. The query behind it still ordered by
createdalone.An order recorded later but settled faster therefore appears below an older row that carries a younger visible date. Before the timestamps were displayed this was invisible; now the ordering openly contradicts what the reader can see. Fiat and crypto rails settle at different speeds, so this is ordinary data, not a corner case.
The cap makes it worse than cosmetic: the hundred rows kept were selected against a different key than the one on display.
The change
getOrdersByUserDatamoves fromfind()to a QueryBuilder, because TypeORM's declarativeorderoption cannot expressCOALESCE. Filtering and loaded relations are unchanged; the ordering is:Three details, each of which cost a review pass:
COALESCEexpression quotes its own columns. In a raworderBystring TypeORM does not translatealias.property, so unquoted camelCase columns would be folded to lower case and fail at runtime.limit, nottake. This is the one that mattered.takesplits the query into an id pass and an entity pass and parses the raworderByat every dot — the expression above became a lookup for an alias namedCOALESCE("custodyOrder", and the method threw on every call. The list would have been dead, not mis-sorted.limitis safe here because every relation joined is to-one, so no row can be duplicated.id. Two orders can share a timestamp. Without it their relative order is whatever the database returns, so rows could swap places between calls or cross the cap and vanish. Both criteria sit before theLIMIT, so the selection itself is deterministic, not just the display.Verification
Gates green. Beyond that, checked end to end against a running local instance with real data: