Skip to content

Commit 7644169

Browse files
committed
Return maximal key when doing a range read
LSM trees use an ordering different than the Haskell `Ord`. This commit enriches the type returned by range reads so that the maximal key is returned, which is then given back to the next iteration of the range reading loop.
1 parent 7f77886 commit 7644169

File tree

20 files changed

+324
-247
lines changed

20 files changed

+324
-247
lines changed

cabal.project

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ index-state:
1616
-- Bump this if you need newer packages from Hackage
1717
, hackage.haskell.org 2025-07-22T09:13:54Z
1818
-- Bump this if you need newer packages from CHaP
19-
, cardano-haskell-packages 2025-08-06T10:58:15Z
19+
, cardano-haskell-packages 2025-08-21T09:41:03Z
2020

2121
packages:
2222
ouroboros-consensus

flake.lock

Lines changed: 21 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ouroboros-consensus-cardano/src/shelley/Ouroboros/Consensus/Shelley/Ledger/Query.hs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,17 +1331,12 @@ answerShelleyTraversingQueries ejTxOut ejTxIn filt cfg q forker = case q of
13311331
)
13321332
vs
13331333

1334-
vnull :: ValuesMK k v -> Bool
1335-
vnull (ValuesMK vs) = Map.null vs
1336-
1337-
toMaxKey (LedgerTables (ValuesMK vs)) = fst $ Map.findMax vs
1338-
13391334
loop queryPredicate !prev !acc = do
1340-
extValues <- LedgerDB.roforkerRangeReadTables forker prev
1341-
if ltcollapse $ ltmap (K2 . vnull) extValues
1342-
then pure acc
1343-
else
1335+
(extValues, k) <- LedgerDB.roforkerRangeReadTables forker prev
1336+
case k of
1337+
Nothing -> pure acc
1338+
Just k' ->
13441339
loop
13451340
queryPredicate
1346-
(PreviousQueryWasUpTo $ toMaxKey extValues)
1341+
(PreviousQueryWasUpTo k')
13471342
(combUtxo acc $ partial queryPredicate extValues)

ouroboros-consensus-diffusion/src/unstable-diffusion-testlib/Test/ThreadNet/Network.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,7 @@ runThreadNetwork
704704
let emptySt = emptySt'
705705
doRangeQuery = roforkerRangeReadTables forker
706706
fullLedgerSt <- fmap ledgerState $ do
707-
fullUTxO <- doRangeQuery NoPreviousQuery
707+
(fullUTxO, _) <- doRangeQuery NoPreviousQuery
708708
pure $! withLedgerTables emptySt fullUTxO
709709
roforkerClose forker
710710
-- Combine the node's seed with the current slot number, to make sure
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!--
2+
A new scriv changelog fragment.
3+
4+
Uncomment the section that is right (remove the HTML comment wrapper).
5+
For top level release notes, leave all the headers commented out.
6+
-->
7+
8+
<!--
9+
### Patch
10+
11+
- A bullet item for the Patch category.
12+
13+
-->
14+
<!--
15+
### Non-Breaking
16+
17+
- A bullet item for the Non-Breaking category.
18+
19+
-->
20+
21+
### Breaking
22+
23+
- `forkerRangeRead` now returns also the maximal key found in the backend.

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Storage/LedgerDB/Forker.hs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import Control.Monad.Except
6666
import Control.Monad.Reader (ReaderT (..))
6767
import Control.Monad.Trans (MonadTrans (..))
6868
import Control.ResourceRegistry
69+
import Data.Bifunctor (first)
6970
import Data.Kind
7071
import Data.Set (Set)
7172
import qualified Data.Set as Set
@@ -105,11 +106,20 @@ data Forker m l blk = Forker
105106

106107
forkerReadTables :: !(LedgerTables l KeysMK -> m (LedgerTables l ValuesMK))
107108
-- ^ Read ledger tables from disk.
108-
, forkerRangeReadTables :: !(RangeQueryPrevious l -> m (LedgerTables l ValuesMK))
109+
, forkerRangeReadTables :: !(RangeQueryPrevious l -> m (LedgerTables l ValuesMK, Maybe (TxIn l)))
109110
-- ^ Range-read ledger tables from disk.
110111
--
111-
-- This range read will return as many values as the 'QueryBatchSize' that
112-
-- was passed when opening the LedgerDB.
112+
-- This range read will return as many values as the 'QueryBatchSize' that was
113+
-- passed when opening the LedgerDB.
114+
--
115+
-- The second component of the returned tuple is the maximal key found by the
116+
-- forker. This is only necessary because some backends have a different
117+
-- sorting for the keys than the order defined in Haskell.
118+
--
119+
-- The last key retrieved is part of the map too. It is intended to be fed
120+
-- back into the next iteration of the range read. If the function returns
121+
-- Nothing, it means the read returned no results, or in other words, we
122+
-- reached the end of the ledger tables.
113123
, forkerGetLedgerState :: !(STM m (l EmptyMK))
114124
-- ^ Get the full ledger state without tables.
115125
--
@@ -206,7 +216,8 @@ ledgerStateReadOnlyForker frk =
206216
ReadOnlyForker
207217
{ roforkerClose = roforkerClose
208218
, roforkerReadTables = fmap castLedgerTables . roforkerReadTables . castLedgerTables
209-
, roforkerRangeReadTables = fmap castLedgerTables . roforkerRangeReadTables . castRangeQueryPrevious
219+
, roforkerRangeReadTables =
220+
fmap (first castLedgerTables) . roforkerRangeReadTables . castRangeQueryPrevious
210221
, roforkerGetLedgerState = ledgerState <$> roforkerGetLedgerState
211222
, roforkerReadStatistics = roforkerReadStatistics
212223
}
@@ -239,7 +250,7 @@ data ReadOnlyForker m l blk = ReadOnlyForker
239250
-- ^ See 'forkerClose'
240251
, roforkerReadTables :: !(LedgerTables l KeysMK -> m (LedgerTables l ValuesMK))
241252
-- ^ See 'forkerReadTables'
242-
, roforkerRangeReadTables :: !(RangeQueryPrevious l -> m (LedgerTables l ValuesMK))
253+
, roforkerRangeReadTables :: !(RangeQueryPrevious l -> m (LedgerTables l ValuesMK, Maybe (TxIn l)))
243254
-- ^ See 'forkerRangeReadTables'.
244255
, roforkerGetLedgerState :: !(STM m (l EmptyMK))
245256
-- ^ See 'forkerGetLedgerState'

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Storage/LedgerDB/V1/BackingStore/API.hs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ module Ouroboros.Consensus.Storage.LedgerDB.V1.BackingStore.API
5858
) where
5959

6060
import Cardano.Slotting.Slot (SlotNo, WithOrigin (..))
61+
import Data.Bifunctor
6162
import Data.Kind
6263
import GHC.Generics
6364
import NoThunks.Class (OnlyCheckWhnfNamed (..))
@@ -93,7 +94,7 @@ data DiffsToFlush l = DiffsToFlush
9394
-- considered as "last flushed" in the kept 'DbChangelog'
9495
}
9596

96-
data BackingStore m keys values diff = BackingStore
97+
data BackingStore m keys key values diff = BackingStore
9798
{ bsClose :: !(m ())
9899
-- ^ Close the backing store
99100
--
@@ -107,7 +108,7 @@ data BackingStore m keys values diff = BackingStore
107108
--
108109
-- The destination path must not already exist. After this operation, it
109110
-- will be a directory.
110-
, bsValueHandle :: !(m (BackingStoreValueHandle m keys values))
111+
, bsValueHandle :: !(m (BackingStoreValueHandle m keys key values))
111112
-- ^ Open a 'BackingStoreValueHandle' capturing the current value of the
112113
-- entire database
113114
, bsWrite :: !(SlotNo -> WriteHint diff -> diff -> m ())
@@ -118,14 +119,15 @@ data BackingStore m keys values diff = BackingStore
118119
}
119120

120121
deriving via
121-
OnlyCheckWhnfNamed "BackingStore" (BackingStore m keys values diff)
122+
OnlyCheckWhnfNamed "BackingStore" (BackingStore m keys key values diff)
122123
instance
123-
NoThunks (BackingStore m keys values diff)
124+
NoThunks (BackingStore m keys key values diff)
124125

125126
type LedgerBackingStore m l =
126127
BackingStore
127128
m
128129
(LedgerTables l KeysMK)
130+
(TxIn l)
129131
(LedgerTables l ValuesMK)
130132
(LedgerTables l DiffMK)
131133

@@ -157,15 +159,15 @@ data InitFrom values
157159
-- The performance cost is usually minimal unless this handle is held open too
158160
-- long. We expect clients of the 'BackingStore' to not retain handles for a
159161
-- long time.
160-
data BackingStoreValueHandle m keys values = BackingStoreValueHandle
162+
data BackingStoreValueHandle m keys key values = BackingStoreValueHandle
161163
{ bsvhAtSlot :: !(WithOrigin SlotNo)
162164
-- ^ At which slot this handle was created
163165
, bsvhClose :: !(m ())
164166
-- ^ Close the handle
165167
--
166168
-- Other methods throw exceptions if called on a closed handle. 'bsvhClose'
167169
-- itself is idempotent.
168-
, bsvhRangeRead :: !(ReadHint values -> RangeQuery keys -> m values)
170+
, bsvhRangeRead :: !(ReadHint values -> RangeQuery keys -> m (values, Maybe key))
169171
-- ^ See 'RangeQuery'
170172
, bsvhReadAll :: !(ReadHint values -> m values)
171173
-- ^ Costly read all operation, not to be used in Consensus but only in
@@ -180,14 +182,15 @@ data BackingStoreValueHandle m keys values = BackingStoreValueHandle
180182
}
181183

182184
deriving via
183-
OnlyCheckWhnfNamed "BackingStoreValueHandle" (BackingStoreValueHandle m keys values)
185+
OnlyCheckWhnfNamed "BackingStoreValueHandle" (BackingStoreValueHandle m keys key values)
184186
instance
185-
NoThunks (BackingStoreValueHandle m keys values)
187+
NoThunks (BackingStoreValueHandle m keys key values)
186188

187189
type LedgerBackingStoreValueHandle m l =
188190
BackingStoreValueHandle
189191
m
190192
(LedgerTables l KeysMK)
193+
(TxIn l)
191194
(LedgerTables l ValuesMK)
192195

193196
type BackingStoreValueHandle' m blk = LedgerBackingStoreValueHandle m (ExtLedgerState blk)
@@ -196,15 +199,16 @@ castBackingStoreValueHandle ::
196199
(Functor m, ReadHint values ~ ReadHint values') =>
197200
(values -> values') ->
198201
(keys' -> keys) ->
199-
BackingStoreValueHandle m keys values ->
200-
BackingStoreValueHandle m keys' values'
201-
castBackingStoreValueHandle f g bsvh =
202+
(key -> key') ->
203+
BackingStoreValueHandle m keys key values ->
204+
BackingStoreValueHandle m keys' key' values'
205+
castBackingStoreValueHandle f g h bsvh =
202206
BackingStoreValueHandle
203207
{ bsvhAtSlot
204208
, bsvhClose
205209
, bsvhReadAll = \rhint -> f <$> bsvhReadAll rhint
206210
, bsvhRangeRead = \rhint (RangeQuery prev count) ->
207-
fmap f . bsvhRangeRead rhint $ RangeQuery (fmap g prev) count
211+
fmap (second (fmap h) . first f) . bsvhRangeRead rhint $ RangeQuery (fmap g prev) count
208212
, bsvhRead = \rhint -> fmap f . bsvhRead rhint . g
209213
, bsvhStat
210214
}
@@ -221,7 +225,7 @@ castBackingStoreValueHandle f g bsvh =
221225
-- | A combination of 'bsValueHandle' and 'bsvhRead'
222226
bsRead ::
223227
MonadThrow m =>
224-
BackingStore m keys values diff ->
228+
BackingStore m keys key values diff ->
225229
ReadHint values ->
226230
keys ->
227231
m (WithOrigin SlotNo, values)
@@ -231,16 +235,16 @@ bsRead store rhint keys = withBsValueHandle store $ \vh -> do
231235

232236
bsReadAll ::
233237
MonadThrow m =>
234-
BackingStore m keys values diff ->
238+
BackingStore m keys key values diff ->
235239
ReadHint values ->
236240
m values
237241
bsReadAll store rhint = withBsValueHandle store $ \vh -> bsvhReadAll vh rhint
238242

239243
-- | A 'IOLike.bracket'ed 'bsValueHandle'
240244
withBsValueHandle ::
241245
MonadThrow m =>
242-
BackingStore m keys values diff ->
243-
(BackingStoreValueHandle m keys values -> m a) ->
246+
BackingStore m keys key values diff ->
247+
(BackingStoreValueHandle m keys key values -> m a) ->
244248
m a
245249
withBsValueHandle store =
246250
bracket

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Storage/LedgerDB/V1/BackingStore/Impl/InMemory.hs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -247,12 +247,14 @@ newInMemoryBackingStore tracer (SnapshotsFS (SomeHasFS fs)) initialization = do
247247
rangeRead ::
248248
RangeQuery (LedgerTables l KeysMK) ->
249249
LedgerTables l ValuesMK ->
250-
LedgerTables l ValuesMK
251-
rangeRead rq values = case rqPrev rq of
252-
Nothing ->
253-
ltmap (rangeRead0' (rqCount rq)) values
254-
Just keys ->
255-
ltliftA2 (rangeRead' (rqCount rq)) keys values
250+
(LedgerTables l ValuesMK, Maybe (TxIn l))
251+
rangeRead rq values =
252+
let vs@(LedgerTables (ValuesMK m)) = case rqPrev rq of
253+
Nothing ->
254+
ltmap (rangeRead0' (rqCount rq)) values
255+
Just keys ->
256+
ltliftA2 (rangeRead' (rqCount rq)) keys values
257+
in (vs, fst <$> Map.lookupMax m)
256258

257259
rangeRead0' ::
258260
Int ->

0 commit comments

Comments
 (0)