From 28954a5909cee691cdd70544ec6b6b7005f1fb2d Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Mon, 26 Sep 2022 18:43:22 -0400 Subject: [PATCH 01/17] add a Table abstraction replacing the Cas abstraction --- .../chainweb-storage/chainweb-storage.cabal | 10 +- .../{Data => Chainweb/Storage}/DedupStore.hs | 27 +-- .../src/Chainweb/Storage/Table.hs | 130 ++++++++++++++ .../src/Chainweb/Storage/Table/Forgetful.hs | 33 ++++ .../src/Chainweb/Storage/Table/HashMap.hs | 66 ++++++++ .../CAS => Chainweb/Storage/Table}/RocksDB.hs | 159 +++++++----------- vendored/chainweb-storage/src/Data/CAS.hs | 137 --------------- .../src/Data/CAS/Forgetful.hs | 32 ---- .../chainweb-storage/src/Data/CAS/HashMap.hs | 68 -------- 9 files changed, 313 insertions(+), 349 deletions(-) rename vendored/chainweb-storage/src/{Data => Chainweb/Storage}/DedupStore.hs (94%) create mode 100644 vendored/chainweb-storage/src/Chainweb/Storage/Table.hs create mode 100644 vendored/chainweb-storage/src/Chainweb/Storage/Table/Forgetful.hs create mode 100644 vendored/chainweb-storage/src/Chainweb/Storage/Table/HashMap.hs rename vendored/chainweb-storage/src/{Data/CAS => Chainweb/Storage/Table}/RocksDB.hs (88%) delete mode 100644 vendored/chainweb-storage/src/Data/CAS.hs delete mode 100644 vendored/chainweb-storage/src/Data/CAS/Forgetful.hs delete mode 100644 vendored/chainweb-storage/src/Data/CAS/HashMap.hs diff --git a/vendored/chainweb-storage/chainweb-storage.cabal b/vendored/chainweb-storage/chainweb-storage.cabal index 65f79241b..bef9c4aa3 100644 --- a/vendored/chainweb-storage/chainweb-storage.cabal +++ b/vendored/chainweb-storage/chainweb-storage.cabal @@ -31,11 +31,11 @@ library ghc-options: -Wall exposed-modules: - Data.CAS - Data.CAS.Forgetful - Data.CAS.HashMap - Data.CAS.RocksDB - Data.DedupStore + Chainweb.Storage.Table + Chainweb.Storage.Table.Forgetful + Chainweb.Storage.Table.HashMap + Chainweb.Storage.Table.RocksDB + Chainweb.Storage.DedupStore build-depends: , base >=4.10 && <4.16 , bytestring >=0.10 diff --git a/vendored/chainweb-storage/src/Data/DedupStore.hs b/vendored/chainweb-storage/src/Chainweb/Storage/DedupStore.hs similarity index 94% rename from vendored/chainweb-storage/src/Data/DedupStore.hs rename to vendored/chainweb-storage/src/Chainweb/Storage/DedupStore.hs index 974f00be9..fb594027f 100644 --- a/vendored/chainweb-storage/src/Data/DedupStore.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/DedupStore.hs @@ -8,9 +8,10 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE FlexibleContexts #-} -- | --- Module: Data.DedupStore +-- Module: Chainweb.Storage.DedupStore -- Copyright: Copyright © 2019 Kadena LLC. -- License: MIT -- Maintainer: Lars Kuhtz @@ -18,7 +19,7 @@ -- -- A Deduplicated Key-Value Store -- -module Data.DedupStore +module Chainweb.Storage.DedupStore ( -- * Deduplicated Key-Value Store DedupStore(..) @@ -46,7 +47,6 @@ import qualified Data.ByteString as B import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Unsafe as BU -import Data.CAS import Data.Hashable import Data.Int import qualified Data.List as L @@ -64,7 +64,8 @@ import System.IO.Unsafe -- internal modules -import Data.CAS.RocksDB +import Chainweb.Storage.Table +import Chainweb.Storage.Table.RocksDB -- -------------------------------------------------------------------------- -- -- Utils @@ -155,7 +156,7 @@ dedupInsert :: DedupStore k v -> k -> v -> IO () dedupInsert store k v = do dedupKey <- dedupStore (_dedupChunks store) $ BL.fromStrict $ _codecEncode (_dedupValueCodec store) v - tableInsert (_dedupRoots store) k dedupKey + tableInsert k dedupKey (_dedupRoots store) {-# INLINE dedupInsert #-} -- | @dedupLookup db k@ returns 'Just' the value at key @k@ in the @@ -164,7 +165,7 @@ dedupInsert store k v = do -- dedupLookup :: DedupStore k v -> k -> IO (Maybe v) dedupLookup store k = do - tableLookup (_dedupRoots store) k >>= \case + tableLookup k (_dedupRoots store) >>= \case Nothing -> return Nothing Just dedupKey -> do dedupRestore (_dedupChunks store) dedupKey >>= \case @@ -185,7 +186,7 @@ instance IsCasValue Chunk where type CasKeyType Chunk = DedupHash casKey (Chunk _ h _) = h -type DedupCas cas = (IsCas cas, CasValueType cas ~ Chunk) +type DedupCas cas = Cas cas Chunk -- | Store a sequence of bytes in a deduplicated content addressed store. -- Returns the hash of the bytes, that can be used to query the data from the @@ -217,7 +218,7 @@ dedupStore' store = go0 where go0 :: HasCallStack => BL.ByteString -> IO (Int, Int, DedupHash) go0 bytes = mapM (hashAndStore 0x0) (toChunks $ roll bytes) >>= \case - [] -> error "Data.DedupStore.dedupStore.go0: Data.ByteString.Lazy.toChunks must not return an empty list" + [] -> error "Chainweb.Storage.DedupStore.dedupStore.go0: Data.ByteString.Lazy.toChunks must not return an empty list" [x] -> return x l -> do (h, m, r) <- go1 (third3 <$> l) @@ -225,7 +226,7 @@ dedupStore' store = go0 go1 :: HasCallStack => [DedupHash] -> IO (Int, Int, DedupHash) go1 hashes = mapM (hashAndStore 0x1) (rollHashes hashes) >>= \case - [] -> error "Data.DedupStore.dedupStore.go1: Data.ByteString.Lazy.toChunks must not return an empty list" + [] -> error "Chainweb.Storage.DedupStore.dedupStore.go1: Data.ByteString.Lazy.toChunks must not return an empty list" [x] -> return x l -> do (h, m, r) <- go1 (third3 <$> l) @@ -245,10 +246,10 @@ dedupStore' store = go0 hashAndStore :: Word8 -> B.ByteString -> IO (Int, Int, DedupHash) hashAndStore tag c = do let h = DedupHash $ BA.convert $ C.hash @_ @DedupHashAlg (B.cons tag c) - (hit, miss) <- casMember store h >>= \x -> if x + (hit, miss) <- tableMember h store >>= \x -> if x then return (1, 0) else do - casInsert store (Chunk tag h c) + casInsert (Chunk tag h c) store return (0, 1) return (hit, miss, h) {-# INLINE hashAndStore #-} @@ -275,7 +276,7 @@ dedupRestore -> DedupHash -> IO (Maybe BL.ByteString) dedupRestore store key = fmap BB.toLazyByteString <$> do - casLookup store key >>= \case + tableLookup key store >>= \case Nothing -> return Nothing Just (Chunk 0x0 _ b) -> return $ Just $ BB.byteString b Just (Chunk 0x1 _ b) -> Just <$> splitHashes b @@ -283,7 +284,7 @@ dedupRestore store key = fmap BB.toLazyByteString <$> do where go h = do - casLookup store h >>= \case + tableLookup h store >>= \case Nothing -> throwM $ DedupStoreCorruptedData "Missing chunk from store" Just (Chunk 0x0 _ b) -> return (BB.byteString b) Just (Chunk 0x1 _ b) -> splitHashes b diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs new file mode 100644 index 000000000..5c739f6e0 --- /dev/null +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs @@ -0,0 +1,130 @@ +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE FunctionalDependencies #-} + +-- | +-- Module: Chainweb.Storage.Table +-- Copyright: Copyright © 2019 Kadena LLC. +-- License: MIT +-- Maintainer: Lars Kuhtz , Edmund Noble +-- Stability: experimental +-- Description: Key Value Store +-- +-- API for Key-Value Stores +-- +module Chainweb.Storage.Table +( IsCasValue(..) +, ReadableTable(..) +, ReadableCas(..) +, Table(..) +, Cas(..) +, casInsert +, IterableTable(..) +, IterableCas(..) +, Entry(..) +, Iterator(..) +, tableLookupM +, TableException(..) +) where + +import Control.Exception (Exception, SomeException) +import Control.Monad.Catch (throwM) + +import Data.Foldable +import Data.Functor +import Data.Kind +import Data.Maybe +import Data.Text (Text) +import qualified Data.Vector as V + +import GHC.Generics +import GHC.Stack + +-- | The class of content-addressable values. +-- +-- The casKey function must be morally injective: +-- +-- prop> casKey a /= casKey b || a == b +-- +-- Usually, 'casKey' is a cryptographic, i.e. collision resistant, hash +-- function. +-- +class Eq (CasKeyType v) => IsCasValue v where + type CasKeyType v + casKey :: v -> CasKeyType v + +-- | Read-Only View of a Content Addressed Key-Value Store +-- +-- Since the key uniquely determines the content of the store a value for a key +-- is either available or not available. There is no dispute about the value +-- itself. +-- +class ReadableTable t k v | t -> k v where + tableLookup :: k -> t -> IO (Maybe v) + tableLookupBatch :: [k] -> t -> IO [Maybe v] + tableLookupBatch ks t = traverse (flip tableLookup t) ks + tableMember :: k -> t -> IO Bool +type ReadableCas t v = ReadableTable t (CasKeyType v) v + +class ReadableTable t k v => Table t k v | t -> k v where + tableInsert :: k -> v -> t -> IO () + tableInsertBatch :: [(k, v)] -> t -> IO () + tableInsertBatch kvs t = traverse_ (flip (uncurry tableInsert) t) kvs + tableDelete :: k -> t -> IO () + tableDeleteBatch :: [k] -> t -> IO () + tableDeleteBatch ks t = traverse_ (flip tableDelete t) ks +type Cas t v = Table t (CasKeyType v) v + +casInsert :: (IsCasValue v, Cas t v) => v -> t -> IO () +casInsert v t = tableInsert (casKey v) v t + +class (Table t k v, Iterator i k v) => IterableTable t i k v | t -> k v, i -> k v where + -- the created iterator must be positioned at the start of the table. + tableCreateIterator :: t -> i +type IterableCas t v = IterableTable t (CasKeyType v) v + +data Entry k v = Entry !k !v + +class Iterator i k v | i -> k v where + iterSeek :: k -> i -> IO () + iterLast :: i -> IO () + iterFirst :: i -> IO () + iterNext :: i -> IO () + iterPrev :: i -> IO () + iterEntry :: i -> IO (Maybe (Entry k v)) + iterKey :: i -> IO (Maybe k) + iterKey i = (fmap.fmap) (\(Entry k _) -> k) $ iterEntry i + iterValue :: i -> IO (Maybe v) + iterValue i = (fmap.fmap) (\(Entry _ v) -> v) $ iterEntry i + iterValid :: i -> IO Bool + iterValid i = isJust <$> iterKey i +type CasIterator i v = Iterator i (CasKeyType v) v + +-- | Lookup a value by its key in a content-addressable store and throw an +-- 'TableException' if the value doesn't exist in the store +-- +tableLookupM :: (HasCallStack, Table t k v) => k -> t -> IO v +tableLookupM cas k = tableLookup cas k >>= \case + Nothing -> throwM . TableException $ + "tableLookupM: lookup failed for table key" + Just v -> return $! v + +casLookupM :: (HasCallStack, Cas t v) => CasKeyType v -> t -> IO v +casLookupM = tableLookupM + +-- | Exceptions that are thrown by instances of 'IsCas'. +-- +data TableException = TableException Text | TableImplementationException SomeException + deriving (Show, Generic) + +instance Exception TableException + diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table/Forgetful.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table/Forgetful.hs new file mode 100644 index 000000000..21b826afd --- /dev/null +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table/Forgetful.hs @@ -0,0 +1,33 @@ +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} + +-- | +-- Module: Chainweb.Storage.Table.Forgetful +-- Copyright: Copyright © 2019 Kadena LLC. +-- License: MIT +-- Maintainer: Lars Kuhtz +-- Stability: experimental +-- +-- A key-value store that forgets everything that is stored in it. +-- +module Chainweb.Storage.Table.Forgetful +( ForgetfulTable(..) +) where + +import Chainweb.Storage.Table + +-- | A key-value store that forgets everything that is added to it. +-- +data ForgetfulTable k v = ForgetfulTable + +instance ReadableTable (ForgetfulTable k v) k v where + tableLookup _ _ = return Nothing + {-# INLINE tableLookup #-} + +instance Table (ForgetfulTable k v) k v where + tableInsert _ _ _ = return () + tableDelete _ _ = return () + {-# INLINE tableInsert #-} + {-# INLINE tableDelete #-} + diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table/HashMap.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table/HashMap.hs new file mode 100644 index 000000000..133f9af61 --- /dev/null +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table/HashMap.hs @@ -0,0 +1,66 @@ +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE FunctionalDependencies #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE UndecidableInstances #-} + +-- | +-- Module: Chainweb.Storage.Table.HashMap +-- Copyright: Copyright © 2019 Kadena LLC. +-- License: MIT +-- Maintainer: Lars Kuhtz +-- Stability: experimental +-- Description: +-- +-- A thread-safe in-memory 'Table' implementation based on 'HM.HashMap'. +-- +module Chainweb.Storage.Table.HashMap +( HashMapTable +, emptyTable +, toList +, size +) where + +import Control.Concurrent.STM.TVar +import Control.Monad ((<$!>)) +import Control.Monad.STM + +import Data.Hashable +import qualified Data.HashMap.Strict as HM + +-- internal modules + +import Chainweb.Storage.Table + +-- | An 'IsTable' implementation that is base on 'HM.HashMap'. +-- +data HashMapTable k v = HashMapTable !(TVar (HM.HashMap k v)) + +instance Hashable k => ReadableTable (HashMapTable k v) k v where + tableLookup k (HashMapTable var) = HM.lookup k <$!> readTVarIO var + {-# INLINE tableLookup #-} + +instance Hashable k => Table (HashMapTable k v) k v where + tableInsert k v table@(HashMapTable var) = + atomically $ modifyTVar' var (HM.insert k v) + tableDelete k table@(HashMapTable var) = + atomically $ modifyTVar' var (HM.delete k) + {-# INLINE tableInsert #-} + {-# INLINE tableDelete #-} + +-- | Create new empty CAS +-- +emptyTable :: Hashable k => IO (HashMapTable k v) +emptyTable = HashMapTable <$> newTVarIO mempty + +-- | Return all entries of CAS as List +-- +toList :: HashMapTable k v -> IO [v] +toList (HashMapTable var) = HM.elems <$!> readTVarIO var + +-- | The number of items in the CAS +-- +size :: HashMapTable k v -> IO Int +size (HashMapTable var) = HM.size <$!> readTVarIO var + diff --git a/vendored/chainweb-storage/src/Data/CAS/RocksDB.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs similarity index 88% rename from vendored/chainweb-storage/src/Data/CAS/RocksDB.hs rename to vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs index ffd08a3e3..be2cb1fc8 100644 --- a/vendored/chainweb-storage/src/Data/CAS/RocksDB.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs @@ -19,15 +19,17 @@ {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE FunctionalDependencies #-} +{-# LANGUAGE TypeSynonymInstances #-} -- | --- Module: Data.CAS.RocksDB +-- Module: Chainweb.Storage.Table.RocksDB -- Copyright: Copyright © 2019 Kadena LLC. -- License: MIT -- Maintainer: Lars Kuhtz -- Stability: experimental -- --- Persisted iey-value and and content-addressable-key-value stores with a +-- Persisted key-value and and content-addressable-key-value stores with a -- RocksDB backend. -- -- A 'RocksDbTable' provides a typed key-value store with an additional iterator @@ -39,7 +41,7 @@ -- TODO: Abstract the 'RocksDbTable' API into a typeclass so that one can -- provide alternative implementations for it. -- -module Data.CAS.RocksDB +module Chainweb.Storage.Table.RocksDB ( RocksDb(..) -- , rocksDbHandle -- , rocksDbNamespace @@ -119,7 +121,7 @@ import qualified Data.ByteString.Unsafe as BU import Data.Coerce import Data.Foldable -import Data.CAS +import Chainweb.Storage.Table import Data.String import qualified Data.Text as T import qualified Data.Vector as V @@ -193,7 +195,7 @@ instance NoThunks RocksDb where [ noThunks ctx (InspectHeapNamed @"Data.RocksDB.Base.DB" a) , noThunks ctx b ] - showTypeOf _ = "Data.CAS.RocksDB.RocksDb" + showTypeOf _ = "Chainweb.Storage.Table.RocksDB.RocksDb" {-# INLINE wNoThunks #-} {-# INLINE showTypeOf #-} @@ -302,7 +304,7 @@ data Codec a = Codec instance NoThunks (Codec a) where -- NoThunks does not look inside of closures for captured thunks wNoThunks _ _ = return Nothing - showTypeOf _ = "Data.CAS.RocksDB.Codec" + showTypeOf _ = "Chainweb.Storage.Table.RocksDB.Codec" {-# INLINE wNoThunks #-} {-# INLINE showTypeOf #-} @@ -323,7 +325,7 @@ instance NoThunks (RocksDbTable k v) where , noThunks ctx c , noThunks ctx (InspectHeapNamed @"Data.RocksDB.Base.DB" d) ] - showTypeOf _ = "Data.CAS.RocksDB.RocksDbTable" + showTypeOf _ = "Chainweb.Storage.Table.RocksDB.RocksDbTable" {-# INLINE wNoThunks #-} {-# INLINE showTypeOf #-} @@ -341,59 +343,13 @@ newTable -> RocksDbTable k v newTable db valCodec keyCodec namespace | any (B8.any (\x -> x `elem` ['$', '%', '/'])) namespace - = error $ "Data.CAS.RocksDb.newTable: invalid character in table namespace: " <> sshow namespace + = error $ "Chainweb.Storage.Table.RocksDb.newTable: invalid character in table namespace: " <> sshow namespace | otherwise = RocksDbTable valCodec keyCodec ns (_rocksDbHandle db) where ns = _rocksDbNamespace db <> "-" <> B.intercalate "/" namespace {-# INLINE newTable #-} --- | @tableInsert db k v@ inserts the value @v@ at key @k@ in the rocks db table --- @db@. --- -tableInsert :: RocksDbTable k v -> k -> v -> IO () -tableInsert db k v = R.put - (_rocksDbTableDb db) - R.defaultWriteOptions - (encKey db k) - (encVal db v) -{-# INLINE tableInsert #-} - --- | @tableLookup db k@ returns 'Just' the value at key @k@ in the --- 'RocksDbTable' @db@ if it exists, or 'Nothing' if the @k@ doesn't exist in --- the table. --- -tableLookup :: RocksDbTable k v -> k -> IO (Maybe v) -tableLookup db k = do - maybeBytes <- get (_rocksDbTableDb db) mempty (encKey db k) - traverse (decVal db) maybeBytes -{-# INLINE tableLookup #-} - --- | @tableLookupBatch db ks@ returns for each @k@ in @ks@ 'Just' the value at --- key @k@ in the 'RocksDbTable' @db@ if it exists, or 'Nothing' if the @k@ --- doesn't exist in the table. --- -tableLookupBatch - :: HasCallStack - => RocksDbTable k v - -> V.Vector k - -> IO (V.Vector (Maybe v)) -tableLookupBatch db ks = do - results <- multiGet (_rocksDbTableDb db) mempty (V.map (encKey db) ks) - V.forM results $ \case - Left e -> error $ "Data.CAS.RocksDB.tableLookupBatch: " <> e - Right x -> traverse (decVal db) x -{-# INLINE tableLookupBatch #-} - --- | @tableDelete db k@ deletes the value at the key @k@ from the 'RocksDbTable' --- db. If the @k@ doesn't exist in @db@ this function does nothing. --- -tableDelete :: RocksDbTable k v -> k -> IO () -tableDelete db k = R.delete - (_rocksDbTableDb db) - R.defaultWriteOptions - (encKey db k) -{-# INLINE tableDelete #-} -- -------------------------------------------------------------------------- -- -- Batches @@ -429,7 +385,7 @@ updateBatch batch = R.write rdb R.defaultWriteOptions $ checkMkOp <$> batch checkMkOp o | rdb == rocksDbUpdateDb o = mkOp o - | otherwise = error "Data.CAS.RocksDB.updateBatch: all operations in a batch must be for the same RocksDB instance." + | otherwise = error "Chainweb.Storage.Table.RocksDB.updateBatch: all operations in a batch must be for the same RocksDB instance." mkOp (RocksDbDelete t k) = R.Del (encKey t k) mkOp (RocksDbInsert t k v) = R.Put (encKey t k) (encVal t v) @@ -462,7 +418,7 @@ instance NoThunks (RocksDbTableIter k v) where , noThunks ctx c , noThunks ctx (InspectHeapNamed @"Data.RocksDB.Iterator.Iterator" d) ] - showTypeOf _ = "Data.CAS.RocksDB.RocksDbTableIterator" + showTypeOf _ = "Chainweb.Storage.Table.RocksDB.RocksDbTableIterator" {-# INLINE wNoThunks #-} {-# INLINE showTypeOf #-} @@ -676,31 +632,47 @@ tableMinEntry = flip withTableIter $ \i -> tableIterFirst i *> tableIterEntry i -- | For a 'IsCasValue' @v@ with 'CasKeyType v ~ k@, a 'RocksDbTable k v' is an -- instance of 'HasCasLookup'. -- -instance (IsCasValue v, CasKeyType v ~ k) => HasCasLookup (RocksDbTable k v) where - type CasValueType (RocksDbTable k v) = v - casLookup = tableLookup - casLookupBatch = tableLookupBatch - - {-# INLINE casLookup #-} - {-# INLINE casLookupBatch #-} +instance ReadableTable (RocksDbTable k v) k v where + tableLookup k db = do + maybeBytes <- get (_rocksDbTableDb db) mempty (encKey db k) + traverse (decVal db) maybeBytes + {-# INLINE tableLookup #-} + + -- | @tableLookupBatch db ks@ returns for each @k@ in @ks@ 'Just' the value at + -- key @k@ in the 'RocksDbTable' @db@ if it exists, or 'Nothing' if the @k@ + -- doesn't exist in the table. + -- + tableLookupBatch ks db = do + results <- V.toList <$> multiGet (_rocksDbTableDb db) mempty (V.fromList $ map (encKey db) ks) + forM results $ \case + Left e -> error $ "Chainweb.Storage.Table.RocksDB.tableLookupBatch: " <> e + Right x -> traverse (decVal db) x + {-# INLINE tableLookupBatch #-} -- | For a 'IsCasValue' @v@ with 'CasKeyType v ~ k@, a 'RocksDbTable k v' is an -- instance of 'IsCas'. -- -instance (IsCasValue v, CasKeyType v ~ k) => IsCas (RocksDbTable k v) where - casInsert db a = tableInsert db (casKey a) a - casDelete = tableDelete +instance Table (RocksDbTable k v) k v where + tableInsert k v db = R.put + (_rocksDbTableDb db) + R.defaultWriteOptions + (encKey db k) + (encVal db v) + {-# INLINE tableInsert #-} - casInsertBatch db vs = updateBatch (mkOp <$> V.toList vs) - where - mkOp v = RocksDbInsert db (casKey v) v + tableDelete k db = R.delete + (_rocksDbTableDb db) + R.defaultWriteOptions + (encKey db k) + {-# INLINE tableDelete #-} - casDeleteBatch db vs = updateBatch (RocksDbDelete db <$> V.toList vs) + tableInsertBatch kvs db = + updateBatch (uncurry (RocksDbInsert db) <$> kvs) + {-# INLINE tableInsertBatch #-} - {-# INLINE casInsert #-} - {-# INLINE casDelete #-} - {-# INLINE casInsertBatch #-} - {-# INLINE casDeleteBatch #-} + tableDeleteBatch ks db = + updateBatch (RocksDbDelete db <$> ks) + {-# INLINE tableDeleteBatch #-} -- | A newtype wrapper that takes only a single type constructor. This useful in -- situations where a Higher Order type constructor for a CAS is required. A @@ -710,24 +682,23 @@ instance (IsCasValue v, CasKeyType v ~ k) => IsCas (RocksDbTable k v) where newtype RocksDbCas v = RocksDbCas { _getRocksDbCas :: RocksDbTable (CasKeyType v) v } deriving newtype (NoThunks) -instance IsCasValue v => HasCasLookup (RocksDbCas v) where - type CasValueType (RocksDbCas v) = v - casLookup (RocksDbCas x) = casLookup x - casLookupBatch (RocksDbCas x) = casLookupBatch x +instance (k ~ CasKeyType v, IsCasValue v) => ReadableTable (RocksDbCas v) k v where + tableLookup k (RocksDbCas x) = tableLookup k x + tableLookupBatch ks (RocksDbCas x) = tableLookupBatch ks x - {-# INLINE casLookup #-} - {-# INLINE casLookupBatch #-} + {-# INLINE tableLookup #-} + {-# INLINE tableLookupBatch #-} -instance IsCasValue v => IsCas (RocksDbCas v) where - casInsert (RocksDbCas x) = casInsert x - casDelete (RocksDbCas x) = casDelete x - casInsertBatch (RocksDbCas x) = casInsertBatch x - casDeleteBatch (RocksDbCas x) = casDeleteBatch x +instance (k ~ CasKeyType v, IsCasValue v) => Table (RocksDbCas v) k v where + tableInsert k v (RocksDbCas x) = tableInsert k v x + tableDelete k (RocksDbCas x) = tableDelete k x + tableInsertBatch kvs (RocksDbCas x) = tableInsertBatch kvs x + tableDeleteBatch ks (RocksDbCas x) = tableDeleteBatch ks x - {-# INLINE casInsert #-} - {-# INLINE casDelete #-} - {-# INLINE casInsertBatch #-} - {-# INLINE casDeleteBatch #-} + {-# INLINE tableInsert #-} + {-# INLINE tableDelete #-} + {-# INLINE tableInsertBatch #-} + {-# INLINE tableDeleteBatch #-} -- | Create a new 'RocksDbCas'. -- @@ -752,7 +723,7 @@ data RocksDbException instance Exception RocksDbException where displayException (RocksDbTableIterInvalidKeyNamespace e a) - = T.unpack $ unexpectedMsg "Data.CAS.RocksDB: invalid table key" e a + = T.unpack $ unexpectedMsg "Chainweb.Storage.Table.RocksDB: invalid table key" e a {-# INLINE displayException #-} instance Show RocksDbException where @@ -811,7 +782,7 @@ checked whatWasIDoing act = alloca $ \errPtr -> do err <- peek errPtr unless (err == nullPtr) $ do errStr <- B.packCString err - let msg = unwords ["Data.CAS.RocksDB.checked: error while", whatWasIDoing <> ":", B8.unpack errStr] + let msg = unwords ["Chainweb.Storage.Table.RocksDB.checked: error while", whatWasIDoing <> ":", B8.unpack errStr] C.c_rocksdb_free err error msg return r @@ -833,7 +804,7 @@ checkpointRocksDb RocksDb { _rocksDbHandle = R.DB dbPtr } logSizeFlushThreshold validateRangeOrdered :: HasCallStack => RocksDbTable k v -> (Maybe k, Maybe k) -> (B.ByteString, B.ByteString) validateRangeOrdered table (Just (encKey table -> l), Just (encKey table -> u)) | l >= u = - error "Data.CAS.RocksDB.validateRangeOrdered: range bounds not ordered according to codec" + error "Chainweb.Storage.Table.RocksDB.validateRangeOrdered: range bounds not ordered according to codec" | otherwise = (l, u) validateRangeOrdered table (l, u) = ( maybe (namespaceFirst (_rocksDbTableNamespace table)) (encKey table) l @@ -849,7 +820,7 @@ deleteRangeRocksDb table range = do R.withCWriteOpts R.defaultWriteOptions $ \optsPtr -> BU.unsafeUseAsCStringLen (fst range') $ \(minKeyPtr, minKeyLen) -> BU.unsafeUseAsCStringLen (snd range') $ \(maxKeyPtr, maxKeyLen) -> - checked "Data.CAS.RocksDB.deleteRangeRocksDb" $ + checked "Chainweb.Storage.Table.RocksDB.deleteRangeRocksDb" $ C.rocksdb_delete_range dbPtr optsPtr minKeyPtr (fromIntegral minKeyLen :: CSize) maxKeyPtr (fromIntegral maxKeyLen :: CSize) @@ -871,7 +842,7 @@ get :: MonadIO m => R.DB -> R.ReadOptions -> ByteString -> m (Maybe ByteString) get (R.DB db_ptr) opts key = liftIO $ R.withReadOptions opts $ \opts_ptr -> BU.unsafeUseAsCStringLen key $ \(key_ptr, klen) -> alloca $ \vlen_ptr -> do - val_ptr <- checked "Data.CAS.RocksDB.get" $ + val_ptr <- checked "Chainweb.Storage.Table.RocksDB.get" $ C.c_rocksdb_get db_ptr opts_ptr key_ptr (R.intToCSize klen) vlen_ptr vlen <- peek vlen_ptr if val_ptr == nullPtr diff --git a/vendored/chainweb-storage/src/Data/CAS.hs b/vendored/chainweb-storage/src/Data/CAS.hs deleted file mode 100644 index 9ebf4fdbd..000000000 --- a/vendored/chainweb-storage/src/Data/CAS.hs +++ /dev/null @@ -1,137 +0,0 @@ -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TypeFamilies #-} --- | --- Module: Data.CAS --- Copyright: Copyright © 2019 Kadena LLC. --- License: MIT --- Maintainer: Lars Kuhtz --- Stability: experimental --- Description: Content Addressed Key Value Store (CAS) --- --- API for Content-Addressable Stores (CAS) --- -module Data.CAS -( IsCasValue(..) -, HasCasLookup(..) -, IsCas(..) -, casLookupM -, HasCasLookupConstraint -, CasConstraint -) where - -import Control.Exception (Exception, SomeException) -import Control.Monad.Catch (throwM) - -import Data.Foldable -import Data.Kind -import Data.Maybe -import Data.Text (Text) -import qualified Data.Vector as V - -import GHC.Generics - --- | The class of content-addressable values. --- --- The casKey function must be morally injective: --- --- prop> casKey a /= casKey b || a == b --- --- Usually, 'casKey' is a cryptographic, i.e. collision resistant, hash --- function. --- -class Eq (CasKeyType v) => IsCasValue v where - type CasKeyType v - casKey :: v -> CasKeyType v - --- | Read-Only View of a Content Addressed Key-Value Store --- --- Since the key uniquely determines the content of the store a value for a key --- is either available or not available. There is no dispute about the value --- itself. --- -class IsCasValue (CasValueType a) => HasCasLookup a where - type CasValueType a :: Type - - -- | Lookup a value in a content-addressable store - -- - casLookup :: a -> CasKeyType (CasValueType a) -> IO (Maybe (CasValueType a)) - - -- | Lookup a batch of values in a content-addressable store - -- - casLookupBatch :: a -> V.Vector (CasKeyType (CasValueType a)) -> IO (V.Vector (Maybe (CasValueType a))) - casLookupBatch = traverse . casLookup - {-# INLINE casLookupBatch #-} - - -- | Check for the existence of a value in a content addressable store - -- - casMember :: a -> CasKeyType (CasValueType a) -> IO Bool - casMember db = fmap isJust . casLookup db - {-# INLINE casMember #-} - - --- | Content Addressed Key-Value Stores --- --- Since the key uniquely determines the content of the store a value for a key --- is either available or not available. There is no dispute about the value --- itself. Thus there are only 'casInsert' and 'casDelete' functions but there --- is no @casUpdate@ function. --- -class HasCasLookup a => IsCas a where - - -- | Insert a value into a content-addressasble store - -- - casInsert :: a -> CasValueType a -> IO () - - -- | Delete a value from a content-addressable store - -- - casDelete :: a -> CasKeyType (CasValueType a) -> IO () - - -- | Insert a batch of values into a content-addressasble store - -- - casInsertBatch :: a -> V.Vector (CasValueType a) -> IO () - casInsertBatch = traverse_ . casInsert - {-# INLINE casInsertBatch #-} - - -- | Delete a batch of values from a content-addressable store - -- - casDeleteBatch :: a -> V.Vector (CasKeyType (CasValueType a)) -> IO () - casDeleteBatch = traverse_ . casDelete - {-# INLINE casDeleteBatch #-} - --- | Lookup a value by its key in a content-addressable store and throw an --- 'CasException' if the value doesn't exist in the store --- -casLookupM - :: HasCasLookup a - => a -> CasKeyType (CasValueType a) -> IO (CasValueType a) -casLookupM cas k = casLookup cas k >>= \case - Nothing -> throwM . CasException $ - "casLookupM: lookup failed for cas key" - (Just !x) -> return x - --- | Exceptions that are thrown by instances of 'IsCas'. --- -data CasException = CasException Text | CasImplementationException SomeException - deriving (Show, Generic) - -instance Exception CasException - --- | @HasCasLookupConstraint cas x@ asserts that @cas x@ is an instance if --- 'HasCasLookup' with value type 'x'. --- -type HasCasLookupConstraint cas x = (HasCasLookup (cas x), CasValueType (cas x) ~ x) - --- | @CasConstraint cas x@ asserts that @cas x@ is an instance if 'IsCas' with --- value type 'x'. --- -type CasConstraint cas x = (HasCasLookupConstraint cas x, IsCas (cas x)) - diff --git a/vendored/chainweb-storage/src/Data/CAS/Forgetful.hs b/vendored/chainweb-storage/src/Data/CAS/Forgetful.hs deleted file mode 100644 index 09432bfd4..000000000 --- a/vendored/chainweb-storage/src/Data/CAS/Forgetful.hs +++ /dev/null @@ -1,32 +0,0 @@ -{-# LANGUAGE TypeFamilies #-} - --- | --- Module: Data.CAS.Forgetful --- Copyright: Copyright © 2019 Kadena LLC. --- License: MIT --- Maintainer: Lars Kuhtz --- Stability: experimental --- --- A content addressable store that forgets everything that is stored in it. --- -module Data.CAS.Forgetful -( ForgetfulCas(..) -) where - -import Data.CAS - --- | A content addressable store that forgets everything that is added to it. --- -data ForgetfulCas a = ForgetfulCas - -instance IsCasValue a => HasCasLookup (ForgetfulCas a) where - type CasValueType (ForgetfulCas a) = a - casLookup _ _ = return Nothing - {-# INLINE casLookup #-} - -instance IsCasValue a => IsCas (ForgetfulCas a) where - casInsert _ _ = return () - casDelete _ _ = return () - {-# INLINE casInsert #-} - {-# INLINE casDelete #-} - diff --git a/vendored/chainweb-storage/src/Data/CAS/HashMap.hs b/vendored/chainweb-storage/src/Data/CAS/HashMap.hs deleted file mode 100644 index 2c7b4eabf..000000000 --- a/vendored/chainweb-storage/src/Data/CAS/HashMap.hs +++ /dev/null @@ -1,68 +0,0 @@ -{-# LANGUAGE ExistentialQuantification #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE UndecidableInstances #-} - --- | --- Module: Data.CAS.HashMap --- Copyright: Copyright © 2019 Kadena LLC. --- License: MIT --- Maintainer: Lars Kuhtz --- Stability: experimental --- Description: --- --- A thread-safe in-memory 'IsCas' implementation based on 'HM.HashMap'. --- -module Data.CAS.HashMap -( HashMapCas -, emptyCas -, toList -, size -) where - -import Control.Concurrent.STM.TVar -import Control.Monad ((<$!>)) -import Control.Monad.STM - -import Data.Hashable -import qualified Data.HashMap.Strict as HM - --- internal modules - -import Data.CAS - --- | An 'IsCas' implementation that is base on 'HM.HashMap'. --- -data HashMapCas v = IsCasValue v => HashMapCas !(TVar (HM.HashMap (CasKeyType v) v)) - -instance (Show (CasKeyType v), Hashable (CasKeyType v), IsCasValue v) => HasCasLookup (HashMapCas v) where - type CasValueType (HashMapCas v) = v - casLookup (HashMapCas var) k = HM.lookup k <$!> readTVarIO var - {-# INLINE casLookup #-} - -instance (Show (CasKeyType v), Hashable (CasKeyType v), IsCasValue v) => IsCas (HashMapCas v) where - casInsert cas@(HashMapCas var) a = casLookup cas (casKey a) >>= \case - Just _ -> return () - Nothing -> atomically $ modifyTVar' var $ HM.insert (casKey a) a - casDelete cas@(HashMapCas var) k = casLookup cas k >>= \case - Nothing -> return () - Just _ -> atomically $ modifyTVar' var $ HM.delete k - {-# INLINE casInsert #-} - {-# INLINE casDelete #-} - --- | Create new empty CAS --- -emptyCas :: Hashable (CasKeyType v) => IsCasValue v => IO (HashMapCas v) -emptyCas = HashMapCas <$> newTVarIO mempty - --- | Return all entries of CAS as List --- -toList :: HashMapCas v -> IO [v] -toList (HashMapCas var) = HM.elems <$!> readTVarIO var - --- | The number of items in the CAS --- -size :: HashMapCas v -> IO Int -size (HashMapCas var) = HM.size <$!> readTVarIO var - From 2bf5a303696a2cc55ce0a7b1670c76c545833e1c Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Mon, 26 Sep 2022 19:43:31 -0400 Subject: [PATCH 02/17] Iterator instance for RocksDB, warnings --- .../src/Chainweb/Storage/Table.hs | 17 +- .../src/Chainweb/Storage/Table/Forgetful.hs | 1 + .../src/Chainweb/Storage/Table/HashMap.hs | 9 +- .../src/Chainweb/Storage/Table/RocksDB.hs | 263 +++++------------- 4 files changed, 88 insertions(+), 202 deletions(-) diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs index 5c739f6e0..3012ff0b6 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs @@ -24,15 +24,17 @@ module Chainweb.Storage.Table ( IsCasValue(..) , ReadableTable(..) -, ReadableCas(..) +, ReadableCas , Table(..) -, Cas(..) +, Cas , casInsert , IterableTable(..) -, IterableCas(..) +, IterableCas , Entry(..) , Iterator(..) +, CasIterator , tableLookupM +, casLookupM , TableException(..) ) where @@ -40,11 +42,8 @@ import Control.Exception (Exception, SomeException) import Control.Monad.Catch (throwM) import Data.Foldable -import Data.Functor -import Data.Kind import Data.Maybe import Data.Text (Text) -import qualified Data.Vector as V import GHC.Generics import GHC.Stack @@ -87,15 +86,15 @@ type Cas t v = Table t (CasKeyType v) v casInsert :: (IsCasValue v, Cas t v) => v -> t -> IO () casInsert v t = tableInsert (casKey v) v t -class (Table t k v, Iterator i k v) => IterableTable t i k v | t -> k v, i -> k v where +class (Table t k v, Iterator i k v) => IterableTable t i k v | t -> k v, i -> k v, t -> i where -- the created iterator must be positioned at the start of the table. - tableCreateIterator :: t -> i + withTableIterator :: t -> (i -> IO a) -> IO a type IterableCas t v = IterableTable t (CasKeyType v) v data Entry k v = Entry !k !v class Iterator i k v | i -> k v where - iterSeek :: k -> i -> IO () + iterSeek :: i -> k -> IO () iterLast :: i -> IO () iterFirst :: i -> IO () iterNext :: i -> IO () diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table/Forgetful.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table/Forgetful.hs index 21b826afd..a1dfff307 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table/Forgetful.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table/Forgetful.hs @@ -23,6 +23,7 @@ data ForgetfulTable k v = ForgetfulTable instance ReadableTable (ForgetfulTable k v) k v where tableLookup _ _ = return Nothing + tableMember _ _ = return False {-# INLINE tableLookup #-} instance Table (ForgetfulTable k v) k v where diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table/HashMap.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table/HashMap.hs index 133f9af61..1310c1959 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table/HashMap.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table/HashMap.hs @@ -39,15 +39,14 @@ data HashMapTable k v = HashMapTable !(TVar (HM.HashMap k v)) instance Hashable k => ReadableTable (HashMapTable k v) k v where tableLookup k (HashMapTable var) = HM.lookup k <$!> readTVarIO var - {-# INLINE tableLookup #-} + tableMember k (HashMapTable var) = + HM.member k <$> readTVarIO var instance Hashable k => Table (HashMapTable k v) k v where - tableInsert k v table@(HashMapTable var) = + tableInsert k v (HashMapTable var) = atomically $ modifyTVar' var (HM.insert k v) - tableDelete k table@(HashMapTable var) = + tableDelete k (HashMapTable var) = atomically $ modifyTVar' var (HM.delete k) - {-# INLINE tableInsert #-} - {-# INLINE tableDelete #-} -- | Create new empty CAS -- diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs index be2cb1fc8..de25d14fd 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs @@ -68,22 +68,6 @@ module Chainweb.Storage.Table.RocksDB -- * Rocks DB Table Iterator , RocksDbTableIter -, withTableIter -, tableIterValid - --- ** Seeking -, tableIterSeek -, tableIterFirst -, tableIterLast - --- ** Advance Iterator -, tableIterNext -, tableIterPrev - --- ** Query Iterator -, tableIterEntry -, tableIterValue -, tableIterKey -- ** Streams , iterToEntryStream @@ -120,6 +104,7 @@ import qualified Data.ByteString.Char8 as B8 import qualified Data.ByteString.Unsafe as BU import Data.Coerce import Data.Foldable +import Data.Maybe import Chainweb.Storage.Table import Data.String @@ -176,7 +161,6 @@ unexpectedMsg msg expected actual = msg -- sshow :: Show a => IsString b => a -> b sshow = fromString . show -{-# INLINE sshow #-} -- -------------------------------------------------------------------------- -- -- RocksDb @@ -196,8 +180,6 @@ instance NoThunks RocksDb where , noThunks ctx b ] showTypeOf _ = "Chainweb.Storage.Table.RocksDB.RocksDb" - {-# INLINE wNoThunks #-} - {-# INLINE showTypeOf #-} -- makeLenses ''RocksDb @@ -305,8 +287,6 @@ instance NoThunks (Codec a) where -- NoThunks does not look inside of closures for captured thunks wNoThunks _ _ = return Nothing showTypeOf _ = "Chainweb.Storage.Table.RocksDB.Codec" - {-# INLINE wNoThunks #-} - {-# INLINE showTypeOf #-} -- | A logical table in a 'RocksDb'. Tables in a rocks db are have isolated key -- namespaces. @@ -326,8 +306,6 @@ instance NoThunks (RocksDbTable k v) where , noThunks ctx (InspectHeapNamed @"Data.RocksDB.Base.DB" d) ] showTypeOf _ = "Chainweb.Storage.Table.RocksDB.RocksDbTable" - {-# INLINE wNoThunks #-} - {-# INLINE showTypeOf #-} -- | Create a new 'RocksDbTable' in the given 'RocksDb'. -- @@ -348,7 +326,6 @@ newTable db valCodec keyCodec namespace = RocksDbTable valCodec keyCodec ns (_rocksDbHandle db) where ns = _rocksDbNamespace db <> "-" <> B.intercalate "/" namespace -{-# INLINE newTable #-} -- -------------------------------------------------------------------------- -- @@ -370,7 +347,6 @@ data RocksDbUpdate rocksDbUpdateDb :: RocksDbUpdate -> R.DB rocksDbUpdateDb (RocksDbDelete t _) = _rocksDbTableDb t rocksDbUpdateDb (RocksDbInsert t _ _) = _rocksDbTableDb t -{-# INLINE rocksDbUpdateDb #-} -- | Atomically execute a batch of rocks db updates. -- @@ -402,7 +378,7 @@ updateBatch batch = R.write rdb R.defaultWriteOptions $ checkMkOp <$> batch -- periods of time. After usage it should be released in a timely manner. -- -- The recommended way to created a 'RocksDbTableIter' is to use the function --- `withTableIter`. +-- `withTableIterator`. -- data RocksDbTableIter k v = RocksDbTableIter { _rocksDbTableIterValueCodec :: !(Codec v) @@ -419,117 +395,59 @@ instance NoThunks (RocksDbTableIter k v) where , noThunks ctx (InspectHeapNamed @"Data.RocksDB.Iterator.Iterator" d) ] showTypeOf _ = "Chainweb.Storage.Table.RocksDB.RocksDbTableIterator" - {-# INLINE wNoThunks #-} - {-# INLINE showTypeOf #-} - --- | Provide an computation with a 'RocksDbTableIterator' and release the iterator --- after after the computation has finished either by returning a result or --- throwing an exception. If the 'RocksDbTable' input is not empty, the iterator --- will point to the first key in the 'RocksdbTable' when created. --- --- This is function provides the preferred way of creating and using a --- 'RocksDbTableIter'. --- -withTableIter :: RocksDbTable k v -> (RocksDbTableIter k v -> IO a) -> IO a -withTableIter db k = R.withReadOptions readOptions $ \opts_ptr -> - I.withIter (_rocksDbTableDb db) opts_ptr $ \iter -> do - let tableIter = makeTableIter iter - tableIterFirst tableIter - k tableIter - where - readOptions = fold - [ R.setLowerBound (namespaceFirst $ _rocksDbTableNamespace db) - , R.setUpperBound (namespaceLast $ _rocksDbTableNamespace db) - -- TODO: this setting tells rocksdb to use prefix seek *when it can*. - -- the question remains: is it actually being used? - , R.setAutoPrefixMode True - ] - makeTableIter = - RocksDbTableIter - (_rocksDbTableValueCodec db) - (_rocksDbTableKeyCodec db) - (_rocksDbTableNamespace db) -{-# INLINE withTableIter #-} - --- | Checks if an 'RocksDbTableIterator' is valid. --- --- A valid iterator returns a value when 'tableIterEntry', 'tableIterValue', or --- 'tableIterKey' is called on it. --- -tableIterValid :: RocksDbTableIter k v -> IO Bool -tableIterValid it = - I.iterValid (_rocksDbTableIter it) -{-# INLINE tableIterValid #-} - --- | Efficiently seek to a key in a 'RocksDbTableIterator' iteration. --- -tableIterSeek :: RocksDbTableIter k v -> k -> IO () -tableIterSeek it = I.iterSeek (_rocksDbTableIter it) . encIterKey it -{-# INLINE tableIterSeek #-} --- | Seek to the first key in a 'RocksDbTable'. --- -tableIterFirst :: RocksDbTableIter k v -> IO () -tableIterFirst it = - I.iterFirst (_rocksDbTableIter it) -{-# INLINE tableIterFirst #-} - --- | Seek to the last value in a 'RocksDbTable' --- -tableIterLast :: RocksDbTableIter k v -> IO () -tableIterLast it = - I.iterLast (_rocksDbTableIter it) -{-# INLINE tableIterLast #-} - --- | Move a 'RocksDbTableIter' to the next key in a 'RocksDbTable'. --- -tableIterNext :: RocksDbTableIter k v -> IO () -tableIterNext = I.iterNext . _rocksDbTableIter -{-# INLINE tableIterNext #-} - --- | Move a 'RocksDbTableIter' to the previous key in a 'RocksDbTable'. --- -tableIterPrev :: RocksDbTableIter k v -> IO () -tableIterPrev = I.iterPrev . _rocksDbTableIter -{-# INLINE tableIterPrev #-} - --- | Returns the key and the value at the current position of a --- 'RocksDbTableIter'. Returns 'Nothing' if the iterator is invalid. --- -tableIterEntry - :: RocksDbTableIter k v - -> IO (Maybe (k, v)) -tableIterEntry it = - I.iterEntry (_rocksDbTableIter it) >>= \case - Nothing -> return Nothing - Just (k, v) -> do - !k' <- decIterKey it k - !v' <- decIterVal it v - return $ Just (k', v') - -{-# INLINE tableIterEntry #-} - --- | Returns the value at the current position of a 'RocksDbTableIter'. Returns --- 'Nothing' if the iterator is invalid. --- -tableIterValue - :: RocksDbTableIter k v - -> IO (Maybe v) -tableIterValue it = I.iterValue (_rocksDbTableIter it) >>= \case - Nothing -> return Nothing - Just v -> pure . Just =<< evaluate =<< decIterVal it v -{-# INLINE tableIterValue #-} - --- | Returns the key at the current position of a 'RocksDbTableIter'. Returns --- 'Nothing' if the iterator is invalid. --- -tableIterKey - :: RocksDbTableIter k v - -> IO (Maybe k) -tableIterKey it = I.iterKey (_rocksDbTableIter it) >>= \case - Nothing -> return Nothing - Just k -> pure . Just =<< evaluate =<< decIterKey it k -{-# INLINE tableIterKey #-} +instance Iterator (RocksDbTableIter k v) k v where + iterValid it = + I.iterValid (_rocksDbTableIter it) + + iterSeek it = I.iterSeek (_rocksDbTableIter it) . encIterKey it + + iterFirst it = + I.iterFirst (_rocksDbTableIter it) + + iterLast it = + I.iterLast (_rocksDbTableIter it) + + iterNext = I.iterNext . _rocksDbTableIter + + iterPrev = I.iterPrev . _rocksDbTableIter + + iterEntry it = + I.iterEntry (_rocksDbTableIter it) >>= \case + Nothing -> return Nothing + Just (k, v) -> do + k' <- decIterKey it k + v' <- decIterVal it v + return $! Just $! Entry k' v' + + iterValue it = I.iterValue (_rocksDbTableIter it) >>= \case + Nothing -> return Nothing + Just v -> pure . Just =<< evaluate =<< decIterVal it v + + iterKey it = I.iterKey (_rocksDbTableIter it) >>= \case + Nothing -> return Nothing + Just k -> pure . Just =<< evaluate =<< decIterKey it k + + +instance IterableTable (RocksDbTable k v) (RocksDbTableIter k v) k v where + withTableIterator db k = R.withReadOptions readOptions $ \opts_ptr -> + I.withIter (_rocksDbTableDb db) opts_ptr $ \iter -> do + let tableIter = makeTableIter iter + iterFirst tableIter + k tableIter + where + readOptions = fold + [ R.setLowerBound (namespaceFirst $ _rocksDbTableNamespace db) + , R.setUpperBound (namespaceLast $ _rocksDbTableNamespace db) + -- TODO: this setting tells rocksdb to use prefix seek *when it can*. + -- the question remains: is it actually being used? + , R.setAutoPrefixMode True + ] + makeTableIter = + RocksDbTableIter + (_rocksDbTableValueCodec db) + (_rocksDbTableKeyCodec db) + (_rocksDbTableNamespace db) -- | Returns the stream of key-value pairs of an 'RocksDbTableIter'. -- @@ -538,12 +456,11 @@ tableIterKey it = I.iterKey (_rocksDbTableIter it) >>= \case -- error. Not releasing the iterator after the processing of the stream has -- finished results in a memory leak. -- -iterToEntryStream :: RocksDbTableIter k v -> S.Stream (S.Of (k,v)) IO () +iterToEntryStream :: RocksDbTableIter k v -> S.Stream (S.Of (Entry k v)) IO () iterToEntryStream it = - liftIO (tableIterEntry it) >>= \case + liftIO (iterEntry it) >>= \case Nothing -> return () - Just x -> S.yield x >> liftIO (tableIterNext it) >> iterToEntryStream it -{-# INLINE iterToEntryStream #-} + Just x -> S.yield x >> liftIO (iterNext it) >> iterToEntryStream it -- | Returns the stream of values of an 'RocksDbTableIter'. -- @@ -554,10 +471,9 @@ iterToEntryStream it = -- iterToValueStream :: Show k => RocksDbTableIter k v -> S.Stream (S.Of v) IO () iterToValueStream it = do - liftIO (tableIterValue it) >>= \case + liftIO (iterValue it) >>= \case Nothing -> return () - Just x -> S.yield x >> liftIO (tableIterNext it) >> iterToValueStream it -{-# INLINE iterToValueStream #-} + Just x -> S.yield x >> liftIO (iterNext it) >> iterToValueStream it -- Returns the stream of key-value pairs of an 'RocksDbTableIter' in reverse -- order. @@ -569,10 +485,9 @@ iterToValueStream it = do -- iterToReverseValueStream :: RocksDbTableIter k v -> S.Stream (S.Of v) IO () iterToReverseValueStream it = - liftIO (tableIterValue it) >>= \case + liftIO (iterValue it) >>= \case Nothing -> return () - Just x -> S.yield x >> liftIO (tableIterPrev it) >> iterToReverseValueStream it -{-# INLINE iterToReverseValueStream #-} + Just x -> S.yield x >> liftIO (iterPrev it) >> iterToReverseValueStream it -- | Returns the stream of keys of an 'RocksDbTableIter'. -- @@ -583,48 +498,41 @@ iterToReverseValueStream it = -- iterToKeyStream :: RocksDbTableIter k v -> S.Stream (S.Of k) IO () iterToKeyStream it = - liftIO (tableIterKey it) >>= \case + liftIO (iterKey it) >>= \case Nothing -> return () - Just x -> S.yield x >> liftIO (tableIterNext it) >> iterToKeyStream it -{-# INLINE iterToKeyStream #-} + Just x -> S.yield x >> liftIO (iterNext it) >> iterToKeyStream it -- Extremal Table Entries -- | The maximum key in a 'RocksDbTable'. -- tableMaxKey :: RocksDbTable k v -> IO (Maybe k) -tableMaxKey = flip withTableIter $ \i -> tableIterLast i *> tableIterKey i -{-# INLINE tableMaxKey #-} +tableMaxKey = flip withTableIterator $ \i -> iterLast i *> iterKey i -- | The maximum value in a 'RocksDbTable'. -- tableMaxValue :: RocksDbTable k v -> IO (Maybe v) -tableMaxValue = flip withTableIter $ \i -> tableIterLast i *> tableIterValue i -{-# INLINE tableMaxValue #-} +tableMaxValue = flip withTableIterator $ \i -> iterLast i *> iterValue i -- | The maximum key-value in a 'RocksDbTable'. -- -tableMaxEntry :: RocksDbTable k v -> IO (Maybe (k, v)) -tableMaxEntry = flip withTableIter $ \i -> tableIterLast i *> tableIterEntry i -{-# INLINE tableMaxEntry #-} +tableMaxEntry :: RocksDbTable k v -> IO (Maybe (Entry k v)) +tableMaxEntry = flip withTableIterator $ \i -> iterLast i *> iterEntry i -- | The minimum key in a 'RocksDbTable'. -- tableMinKey :: RocksDbTable k v -> IO (Maybe k) -tableMinKey = flip withTableIter $ \i -> tableIterFirst i *> tableIterKey i -{-# INLINE tableMinKey #-} +tableMinKey = flip withTableIterator $ \i -> iterFirst i *> iterKey i -- | The minimum value in a 'RocksDbTable'. -- tableMinValue :: RocksDbTable k v -> IO (Maybe v) -tableMinValue = flip withTableIter $ \i -> tableIterFirst i *> tableIterValue i -{-# INLINE tableMinValue #-} +tableMinValue = flip withTableIterator $ \i -> iterFirst i *> iterValue i -- | The minimum key-value in a 'RocksDbTable'. -- -tableMinEntry :: RocksDbTable k v -> IO (Maybe (k, v)) -tableMinEntry = flip withTableIter $ \i -> tableIterFirst i *> tableIterEntry i -{-# INLINE tableMinEntry #-} +tableMinEntry :: RocksDbTable k v -> IO (Maybe (Entry k v)) +tableMinEntry = flip withTableIterator $ \i -> iterFirst i *> iterEntry i -- -------------------------------------------------------------------------- -- -- CAS @@ -636,7 +544,9 @@ instance ReadableTable (RocksDbTable k v) k v where tableLookup k db = do maybeBytes <- get (_rocksDbTableDb db) mempty (encKey db k) traverse (decVal db) maybeBytes - {-# INLINE tableLookup #-} + + tableMember k db = + isJust <$> get (_rocksDbTableDb db) mempty (encKey db k) -- | @tableLookupBatch db ks@ returns for each @k@ in @ks@ 'Just' the value at -- key @k@ in the 'RocksDbTable' @db@ if it exists, or 'Nothing' if the @k@ @@ -647,7 +557,6 @@ instance ReadableTable (RocksDbTable k v) k v where forM results $ \case Left e -> error $ "Chainweb.Storage.Table.RocksDB.tableLookupBatch: " <> e Right x -> traverse (decVal db) x - {-# INLINE tableLookupBatch #-} -- | For a 'IsCasValue' @v@ with 'CasKeyType v ~ k@, a 'RocksDbTable k v' is an -- instance of 'IsCas'. @@ -658,21 +567,17 @@ instance Table (RocksDbTable k v) k v where R.defaultWriteOptions (encKey db k) (encVal db v) - {-# INLINE tableInsert #-} tableDelete k db = R.delete (_rocksDbTableDb db) R.defaultWriteOptions (encKey db k) - {-# INLINE tableDelete #-} tableInsertBatch kvs db = updateBatch (uncurry (RocksDbInsert db) <$> kvs) - {-# INLINE tableInsertBatch #-} tableDeleteBatch ks db = updateBatch (RocksDbDelete db <$> ks) - {-# INLINE tableDeleteBatch #-} -- | A newtype wrapper that takes only a single type constructor. This useful in -- situations where a Higher Order type constructor for a CAS is required. A @@ -685,9 +590,7 @@ newtype RocksDbCas v = RocksDbCas { _getRocksDbCas :: RocksDbTable (CasKeyType v instance (k ~ CasKeyType v, IsCasValue v) => ReadableTable (RocksDbCas v) k v where tableLookup k (RocksDbCas x) = tableLookup k x tableLookupBatch ks (RocksDbCas x) = tableLookupBatch ks x - - {-# INLINE tableLookup #-} - {-# INLINE tableLookupBatch #-} + tableMember k (RocksDbCas x) = tableMember k x instance (k ~ CasKeyType v, IsCasValue v) => Table (RocksDbCas v) k v where tableInsert k v (RocksDbCas x) = tableInsert k v x @@ -695,11 +598,6 @@ instance (k ~ CasKeyType v, IsCasValue v) => Table (RocksDbCas v) k v where tableInsertBatch kvs (RocksDbCas x) = tableInsertBatch kvs x tableDeleteBatch ks (RocksDbCas x) = tableDeleteBatch ks x - {-# INLINE tableInsert #-} - {-# INLINE tableDelete #-} - {-# INLINE tableInsertBatch #-} - {-# INLINE tableDeleteBatch #-} - -- | Create a new 'RocksDbCas'. -- newCas @@ -710,7 +608,6 @@ newCas -> [B.ByteString] -> RocksDbCas v newCas db vc kc n = RocksDbCas $ newTable db vc kc n -{-# INLINE newCas #-} -- -------------------------------------------------------------------------- -- -- Exceptions @@ -724,56 +621,46 @@ data RocksDbException instance Exception RocksDbException where displayException (RocksDbTableIterInvalidKeyNamespace e a) = T.unpack $ unexpectedMsg "Chainweb.Storage.Table.RocksDB: invalid table key" e a - {-# INLINE displayException #-} instance Show RocksDbException where show = displayException - {-# INLINE show #-} -- -------------------------------------------------------------------------- -- -- Table Utils encVal :: RocksDbTable k v -> v -> B.ByteString encVal = _codecEncode . _rocksDbTableValueCodec -{-# INLINE encVal #-} encKey :: RocksDbTable k v -> k -> B.ByteString encKey it k = namespaceFirst ns <> _codecEncode (_rocksDbTableKeyCodec it) k where ns = _rocksDbTableNamespace it -{-# INLINE encKey #-} decVal :: MonadThrow m => RocksDbTable k v -> B.ByteString -> m v decVal tbl = _codecDecode $ _rocksDbTableValueCodec tbl -{-# INLINE decVal #-} -- -------------------------------------------------------------------------- -- -- Iter Utils namespaceFirst :: B.ByteString -> B.ByteString namespaceFirst ns = ns <> "$" -{-# INLINE namespaceFirst #-} namespaceLast :: B.ByteString -> B.ByteString namespaceLast ns = ns <> "%" -{-# INLINE namespaceLast #-} encIterKey :: RocksDbTableIter k v -> k -> B.ByteString encIterKey it k = namespaceFirst ns <> _codecEncode (_rocksDbTableIterKeyCodec it) k where ns = _rocksDbTableIterNamespace it -{-# INLINE encIterKey #-} decIterVal :: MonadThrow m => RocksDbTableIter k v -> B.ByteString -> m v decIterVal i bs = _codecDecode (_rocksDbTableIterValueCodec i) bs -{-# INLINE decIterVal #-} decIterKey :: MonadThrow m => RocksDbTableIter k v -> B.ByteString -> m k decIterKey it k = _codecDecode (_rocksDbTableIterKeyCodec it) (B.drop (B.length prefix) k) where prefix = namespaceFirst $ _rocksDbTableIterNamespace it -{-# INLINE decIterKey #-} checked :: HasCallStack => String -> (Ptr CString -> IO a) -> IO a checked whatWasIDoing act = alloca $ \errPtr -> do From a6fbf307e1ea497d68e8166b9e414616cec545da Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Tue, 27 Sep 2022 15:07:35 -0400 Subject: [PATCH 03/17] fix tests --- .../src/Chainweb/Storage/Table.hs | 80 +++--- .../src/Chainweb/Storage/Table/RocksDB.hs | 117 +++++---- vendored/chainweb-storage/test/Main.hs | 237 +++++++++--------- 3 files changed, 226 insertions(+), 208 deletions(-) diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs index 3012ff0b6..b5ad5a14a 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs @@ -1,15 +1,12 @@ -{-# LANGUAGE BangPatterns #-} {-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE GADTs #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE FunctionalDependencies #-} -- | -- Module: Chainweb.Storage.Table @@ -20,23 +17,26 @@ -- Description: Key Value Store -- -- API for Key-Value Stores --- module Chainweb.Storage.Table -( IsCasValue(..) -, ReadableTable(..) -, ReadableCas -, Table(..) -, Cas -, casInsert -, IterableTable(..) -, IterableCas -, Entry(..) -, Iterator(..) -, CasIterator -, tableLookupM -, casLookupM -, TableException(..) -) where + ( IsCasValue (..), + ReadableTable (..), + ReadableCas, + Table (..), + Cas, + casInsert, + casInsertBatch, + casDelete, + casDeleteBatch, + IterableTable (..), + IterableCas, + Entry (..), + Iterator (..), + CasIterator, + tableLookupM, + casLookupM, + TableException (..), + ) +where import Control.Exception (Exception, SomeException) import Control.Monad.Catch (throwM) @@ -56,7 +56,6 @@ import GHC.Stack -- -- Usually, 'casKey' is a cryptographic, i.e. collision resistant, hash -- function. --- class Eq (CasKeyType v) => IsCasValue v where type CasKeyType v casKey :: v -> CasKeyType v @@ -66,12 +65,12 @@ class Eq (CasKeyType v) => IsCasValue v where -- Since the key uniquely determines the content of the store a value for a key -- is either available or not available. There is no dispute about the value -- itself. --- class ReadableTable t k v | t -> k v where tableLookup :: k -> t -> IO (Maybe v) tableLookupBatch :: [k] -> t -> IO [Maybe v] tableLookupBatch ks t = traverse (flip tableLookup t) ks tableMember :: k -> t -> IO Bool + type ReadableCas t v = ReadableTable t (CasKeyType v) v class ReadableTable t k v => Table t k v | t -> k v where @@ -81,17 +80,29 @@ class ReadableTable t k v => Table t k v | t -> k v where tableDelete :: k -> t -> IO () tableDeleteBatch :: [k] -> t -> IO () tableDeleteBatch ks t = traverse_ (flip tableDelete t) ks + type Cas t v = Table t (CasKeyType v) v casInsert :: (IsCasValue v, Cas t v) => v -> t -> IO () -casInsert v t = tableInsert (casKey v) v t +casInsert v = tableInsert (casKey v) v + +casInsertBatch :: (IsCasValue v, Cas t v) => [v] -> t -> IO () +casInsertBatch vs = tableInsertBatch [(casKey v, v) | v <- vs] + +casDelete :: (IsCasValue v, Cas t v) => v -> t -> IO () +casDelete = tableDelete . casKey + +casDeleteBatch :: (IsCasValue v, Cas t v) => [v] -> t -> IO () +casDeleteBatch = tableDeleteBatch . fmap casKey class (Table t k v, Iterator i k v) => IterableTable t i k v | t -> k v, i -> k v, t -> i where -- the created iterator must be positioned at the start of the table. withTableIterator :: t -> (i -> IO a) -> IO a + type IterableCas t v = IterableTable t (CasKeyType v) v data Entry k v = Entry !k !v + deriving (Eq, Show, Ord) class Iterator i k v | i -> k v where iterSeek :: i -> k -> IO () @@ -101,29 +112,32 @@ class Iterator i k v | i -> k v where iterPrev :: i -> IO () iterEntry :: i -> IO (Maybe (Entry k v)) iterKey :: i -> IO (Maybe k) - iterKey i = (fmap.fmap) (\(Entry k _) -> k) $ iterEntry i + iterKey i = (fmap . fmap) (\(Entry k _) -> k) $ iterEntry i iterValue :: i -> IO (Maybe v) - iterValue i = (fmap.fmap) (\(Entry _ v) -> v) $ iterEntry i + iterValue i = (fmap . fmap) (\(Entry _ v) -> v) $ iterEntry i iterValid :: i -> IO Bool iterValid i = isJust <$> iterKey i + type CasIterator i v = Iterator i (CasKeyType v) v -- | Lookup a value by its key in a content-addressable store and throw an -- 'TableException' if the value doesn't exist in the store --- tableLookupM :: (HasCallStack, Table t k v) => k -> t -> IO v -tableLookupM cas k = tableLookup cas k >>= \case - Nothing -> throwM . TableException $ - "tableLookupM: lookup failed for table key" - Just v -> return $! v +tableLookupM cas k = + tableLookup cas k >>= \case + Nothing -> + throwM . TableException $ + "tableLookupM: lookup failed for table key" + Just v -> return $! v casLookupM :: (HasCallStack, Cas t v) => CasKeyType v -> t -> IO v casLookupM = tableLookupM -- | Exceptions that are thrown by instances of 'IsCas'. --- -data TableException = TableException Text | TableImplementationException SomeException - deriving (Show, Generic) +data TableException + = TableException !Text + | TableImplementationException !SomeException + deriving (Show, Generic) instance Exception TableException diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs index de25d14fd..8561609bc 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs @@ -3,24 +3,20 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE DerivingVia #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE UndecidableInstances #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE FunctionalDependencies #-} -{-# LANGUAGE TypeSynonymInstances #-} +{-# LANGUAGE ViewPatterns #-} -- | -- Module: Chainweb.Storage.Table.RocksDB @@ -42,56 +38,56 @@ -- provide alternative implementations for it. -- module Chainweb.Storage.Table.RocksDB -( RocksDb(..) --- , rocksDbHandle --- , rocksDbNamespace -, openRocksDb -, closeRocksDb -, withRocksDb -, withTempRocksDb -, destroyRocksDb -, resetOpenRocksDb -, modernDefaultOptions - --- * Rocks DB Table -, Codec(..) -, RocksDbTable -, newTable -, tableLookup -, tableLookupBatch -, tableInsert -, tableDelete - --- * Batch Updates -, RocksDbUpdate(..) -, updateBatch - --- * Rocks DB Table Iterator -, RocksDbTableIter - --- ** Streams -, iterToEntryStream -, iterToValueStream -, iterToReverseValueStream -, iterToKeyStream - --- ** Extremal Table Entries -, tableMaxKey -, tableMaxValue -, tableMaxEntry -, tableMinKey -, tableMinValue -, tableMinEntry - --- * RocksDbCas -, RocksDbCas(..) -, newCas - --- * RocksDB-specific tools -, checkpointRocksDb -, deleteRangeRocksDb -, compactRangeRocksDb -) where + ( RocksDb(..) + -- , rocksDbHandle + -- , rocksDbNamespace + , openRocksDb + , closeRocksDb + , withRocksDb + , withTempRocksDb + , destroyRocksDb + , resetOpenRocksDb + , modernDefaultOptions + + -- * Rocks DB Table + , Codec(..) + , RocksDbTable + , newTable + , tableLookup + , tableLookupBatch + , tableInsert + , tableDelete + + -- * Batch Updates + , RocksDbUpdate(..) + , updateBatch + + -- * Rocks DB Table Iterator + , RocksDbTableIter + + -- ** Streams + , iterToEntryStream + , iterToValueStream + , iterToReverseValueStream + , iterToKeyStream + + -- ** Extremal Table Entries + , tableMaxKey + , tableMaxValue + , tableMaxEntry + , tableMinKey + , tableMinValue + , tableMinEntry + + -- * RocksDbCas + , RocksDbCas(..) + , newCas + + -- * RocksDB-specific tools + , checkpointRocksDb + , deleteRangeRocksDb + , compactRangeRocksDb + ) where import Control.Exception(evaluate) import Control.Monad @@ -199,7 +195,7 @@ openRocksDb path opts_ptr = do GHC.setFileSystemEncoding GHC.utf8 createDirectoryIfMissing True path db <- withFilePath path $ \path_ptr -> - liftM R.DB + fmap R.DB $ R.throwIfErr "open" $ C.c_rocksdb_open opts_ptr path_ptr let rdb = RocksDb db mempty @@ -422,12 +418,11 @@ instance Iterator (RocksDbTableIter k v) k v where iterValue it = I.iterValue (_rocksDbTableIter it) >>= \case Nothing -> return Nothing - Just v -> pure . Just =<< evaluate =<< decIterVal it v + Just v -> Just <$> (evaluate =<< decIterVal it v) iterKey it = I.iterKey (_rocksDbTableIter it) >>= \case Nothing -> return Nothing - Just k -> pure . Just =<< evaluate =<< decIterKey it k - + Just k -> Just <$> (evaluate =<< decIterKey it k) instance IterableTable (RocksDbTable k v) (RocksDbTableIter k v) k v where withTableIterator db k = R.withReadOptions readOptions $ \opts_ptr -> @@ -654,7 +649,7 @@ encIterKey it k = namespaceFirst ns <> _codecEncode (_rocksDbTableIterKeyCodec i ns = _rocksDbTableIterNamespace it decIterVal :: MonadThrow m => RocksDbTableIter k v -> B.ByteString -> m v -decIterVal i bs = _codecDecode (_rocksDbTableIterValueCodec i) bs +decIterVal i = _codecDecode (_rocksDbTableIterValueCodec i) decIterKey :: MonadThrow m => RocksDbTableIter k v -> B.ByteString -> m k decIterKey it k = diff --git a/vendored/chainweb-storage/test/Main.hs b/vendored/chainweb-storage/test/Main.hs index 5331cec28..8c9e73118 100644 --- a/vendored/chainweb-storage/test/Main.hs +++ b/vendored/chainweb-storage/test/Main.hs @@ -3,7 +3,6 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} - {-# OPTIONS_GHC -fno-warn-orphans #-} -- | @@ -13,57 +12,71 @@ -- Maintainer: Lars Kuhtz -- Stability: experimental -- --- Tests for "Data.CAS.RocksDB" --- +-- Tests for "Chainweb.Storage.RocksDB" module Main -( main -) where + ( main, + ) +where + +-- internal modules +import Chainweb.Storage.Table +import Chainweb.Storage.Table.RocksDB import Control.Concurrent.Async import Control.Exception import Control.Lens import Control.Monad import Control.Monad.Catch - import qualified Data.ByteString.Char8 as B8 import Data.Foldable import Data.List - import GHC.Stack - import NoThunks.Class - import Text.Read -import qualified Data.Vector as V - --- internal modules - -import Data.CAS -import Data.CAS.RocksDB - -- -------------------------------------------------------------------------- -- -- Utils data RocksDbTableTestException - = CodecException String - | RocksDbTableTestFailure String - deriving (Show) + = CodecException !String + | RocksDbTableTestFailure !String + +instance Show RocksDbTableTestException where + show (CodecException str) = + unlines + [ "Codec exception" + , str + ] + show (RocksDbTableTestFailure str) = + unlines + [ "RocksDb table test failure" + , str + ] instance Exception RocksDbTableTestException assertIO :: HasCallStack => Eq a => Show a => IO a -> a -> IO () -assertIO f r = f >>= \a -> unless (a == r) $ throwM - $ RocksDbTableTestFailure - $ "test failed:\n expected: " <> show r <> "\n actual: " <> show a - <> " " <> prettyCallStack callStack +assertIO f r = + f >>= \a -> + unless (a == r) $ + throwM $ + RocksDbTableTestFailure $ unlines + [ "test failed:" + , unwords ["expected:", show r] + , unwords ["actual:", show a] + , prettyCallStack callStack + ] assertNoThunks :: HasCallStack => NoThunks a => a -> IO () -assertNoThunks !a = case unsafeNoThunks $! a of - Nothing -> return () - Just e -> throwM $ RocksDbTableTestFailure - $ "test failed:\n unexpected thunk: " <> show e - <> " " <> prettyCallStack callStack +assertNoThunks a = case unsafeNoThunks $! a of + Nothing -> return () + Just e -> + throwM $ + RocksDbTableTestFailure $ unlines + [ "test failed:" + , unwords ["unexpected thunk:", show e] + , prettyCallStack callStack + ] -- -------------------------------------------------------------------------- -- -- Test Table @@ -82,41 +95,40 @@ intTable db tableName = newTable db intCodec intCodec [tableName] assertEmptyTable :: HasCallStack => RocksDbTable Int Int -> IO () assertEmptyTable t = do assertNoThunks t - assertIO (tableLookup t 1) Nothing + assertIO (tableLookup 1 t) Nothing assertEntries t [] assertEntries :: HasCallStack => RocksDbTable Int Int -> [(Int, Int)] -> IO () assertEntries t l_ = do assertNoThunks t - forM_ l $ \(k,v) -> assertIO (tableLookup t k) (Just v) - assertIO (tableLookupBatch t (V.fromList ks)) (Just <$> V.fromList vs) + forM_ l $ \(k, v) -> assertIO (tableLookup k t) (Just v) + assertIO (tableLookupBatch ks t) (Just <$> vs) - assertIO (tableMinKey t) (firstOf (folded._1) l) - assertIO (tableMinValue t) (firstOf (folded._2) l) + assertIO (tableMinKey t) (firstOf (folded . _1) l) + assertIO (tableMinValue t) (firstOf (folded . _2) l) - assertIO (tableMaxKey t) (lastOf (folded._1) l) - assertIO (tableMaxValue t) (lastOf (folded._2) l) + assertIO (tableMaxKey t) (lastOf (folded . _1) l) + assertIO (tableMaxValue t) (lastOf (folded . _2) l) -- check forward iteration and first and last - withTableIter t $ \i -> do - assertIO (tableIterFirst i >> tableIterKey i) (firstOf (folded._1) l) - assertIO (tableIterLast i >> tableIterKey i) (lastOf (folded._1) l) - - tableIterFirst i - assertIO (tableIterValid i) (not $ null l) - forM_ l $ \(k,v) -> do - assertIO (tableIterEntry i) (Just (k,v)) - tableIterNext i - assertIO (tableIterValid i) False - - -- check backward iteration - withTableIter t $ \i -> do - tableIterLast i - assertIO (tableIterValid i) (not $ null l) - forM_ (reverse l) $ \(k,v) -> do - assertIO (tableIterEntry i) (Just (k,v)) - tableIterPrev i - assertIO (tableIterValid i) False + withTableIterator t $ \i -> do + assertIO (iterFirst i >> iterKey i) (firstOf (folded . _1) l) + assertIO (iterLast i >> iterKey i) (lastOf (folded . _1) l) + iterFirst i + assertIO (iterValid i) (not $ null l) + forM_ l $ \(k, v) -> do + assertIO (iterEntry i) (Just (Entry k v)) + iterNext i + assertIO (iterValid i) False + + -- check backward iteration + withTableIterator t $ \i -> do + iterLast i + assertIO (iterValid i) (not $ null l) + forM_ (reverse l) $ \(k, v) -> do + assertIO (iterEntry i) (Just (Entry k v)) + iterPrev i + assertIO (iterValid i) False where l = sort l_ (ks, vs) = unzip l @@ -126,19 +138,19 @@ tableTests db tableName = do assertNoThunks t assertEmptyTable t - tableInsert t 1 8 - assertEntries t [(1,8)] + tableInsert 1 8 t + assertEntries t [(1, 8)] - tableInsert t 2 9 - assertEntries t [(1,8), (2,9)] + tableInsert 2 9 t + assertEntries t [(1, 8), (2, 9)] - tableDelete t 1 - assertEntries t [(2,9)] + tableDelete 1 t + assertEntries t [(2, 9)] - tableInsert t 2 8 - assertEntries t [(2,8)] + tableInsert 2 8 t + assertEntries t [(2, 8)] - tableDelete t 2 + tableDelete 2 t assertEmptyTable t where !t = intTable db tableName @@ -151,81 +163,79 @@ tableBatchTests db tableName = do updateBatch [] assertEmptyTable t - updateBatch [ RocksDbInsert t 1 8] - assertEntries t [(1,8)] + updateBatch [RocksDbInsert t 1 8] + assertEntries t [(1, 8)] updateBatch [RocksDbInsert t 2 9] - assertEntries t [(1,8), (2,9)] + assertEntries t [(1, 8), (2, 9)] updateBatch [RocksDbDelete t 2] - assertEntries t [(1,8)] + assertEntries t [(1, 8)] updateBatch [RocksDbInsert t 2 9, RocksDbDelete t 2] - assertEntries t [(1,8)] + assertEntries t [(1, 8)] updateBatch [RocksDbInsert t 2 9, RocksDbDelete t 2, RocksDbInsert t 2 9] - assertEntries t [(1,8), (2,9)] + assertEntries t [(1, 8), (2, 9)] updateBatch [RocksDbInsert t 1 8, RocksDbDelete t 1] - assertEntries t [(2,9)] + assertEntries t [(2, 9)] updateBatch [RocksDbInsert t 1 7, RocksDbInsert t 1 8, RocksDbInsert t 1 8] - assertEntries t [(1,8), (2,9)] + assertEntries t [(1, 8), (2, 9)] updateBatch [RocksDbDelete t 1, RocksDbInsert t 3 7] - assertEntries t [(2,9), (3,7)] + assertEntries t [(2, 9), (3, 7)] updateBatch [RocksDbInsert t 4 6, RocksDbInsert t 5 5] - assertEntries t [(2,9), (3,7), (4,6), (5,5)] + assertEntries t [(2, 9), (3, 7), (4, 6), (5, 5)] updateBatch [RocksDbDelete t 2, RocksDbDelete t 3, RocksDbDelete t 4, RocksDbDelete t 5] assertEmptyTable t where t = intTable db tableName - -- Orphan instance -- instance IsCasValue Int where type CasKeyType Int = Int - casKey = (+10) - {-# INLINE casKey #-} + casKey = (+ 10) casBatchTests :: HasCallStack => RocksDb -> B8.ByteString -> IO () casBatchTests db tableName = do assertEmptyTable t - casInsertBatch t mempty + tableInsertBatch mempty t assertEmptyTable t - casInsertBatch t [1] + casInsertBatch [1] t assertCasEntries t [1] - casInsertBatch t [2] + casInsertBatch [2] t assertCasEntries t [1, 2] - casDeleteBatch t [casKey @Int 2] + casDeleteBatch [2] t assertCasEntries t [1] - casInsertBatch t [1] + casInsertBatch [1] t assertCasEntries t [1] - casInsertBatch t [2, 2, 2] + casInsertBatch [2, 2, 2] t assertCasEntries t [1, 2] - casInsertBatch t [1,2,3,4] + casInsertBatch [1, 2, 3, 4] t assertCasEntries t [1, 2, 3, 4] - casDeleteBatch t [casKey @Int 5] + casDeleteBatch [5] t assertCasEntries t [1, 2, 3, 4] - casDeleteBatch t $ casKey @Int <$> [1, 3, 1] + casDeleteBatch [1, 3, 1] t assertCasEntries t [2, 4] - casDeleteBatch t [] + casDeleteBatch [] t assertCasEntries t [2, 4] - casDeleteBatch t $ casKey @Int <$> [2, 4] + casDeleteBatch [2, 4] t assertEmptyTable t where t = intTable db tableName @@ -233,49 +243,49 @@ casBatchTests db tableName = do casTests :: HasCallStack => RocksDb -> B8.ByteString -> IO () casTests db tableName = do assertEmptyTable t - assertIO (casMember t 1) False - assertIO (casLookup t 1) Nothing + assertIO (tableMember 1 t) False + assertIO (tableLookup 1 t) Nothing - casInsertBatch t mempty + casInsertBatch mempty t assertEmptyTable t - casInsert t 1 + casInsert 1 t assertCasEntries t [1] - assertIO (casMember t $ casKey @Int 1) True - assertIO (casLookup t $ casKey @Int 1) (Just 1) + assertIO (tableMember (casKey @Int 1) t) True + assertIO (tableLookup (casKey @Int 1) t) (Just 1) - casInsert t 2 + casInsert 2 t assertCasEntries t [1, 2] - assertIO (casMember t $ casKey @Int 1) True - assertIO (casMember t $ casKey @Int 2) True - assertIO (casLookup t $ casKey @Int 1) (Just 1) - assertIO (casLookup t $ casKey @Int 2) (Just 2) - assertIO (casLookupBatch t [casKey @Int 1, casKey @Int 2]) [Just 1, Just 2] + assertIO (tableMember (casKey @Int 1) t) True + assertIO (tableMember (casKey @Int 2) t) True + assertIO (tableLookup (casKey @Int 1) t) (Just 1) + assertIO (tableLookup (casKey @Int 2) t) (Just 2) + assertIO (tableLookupBatch [casKey @Int 1, casKey @Int 2] t) [Just 1, Just 2] - casDelete t $ casKey @Int 2 + casDelete 2 t assertCasEntries t [1] - assertIO (casMember t $ casKey @Int 1) True - assertIO (casMember t $ casKey @Int 2) False - assertIO (casLookup t $ casKey @Int 1) (Just 1) - assertIO (casLookup t $ casKey @Int 2) Nothing - assertIO (casLookupBatch t [casKey @Int 1, casKey @Int 2]) [Just 1, Nothing] + assertIO (tableMember (casKey @Int 1) t) True + assertIO (tableMember (casKey @Int 2) t) False + assertIO (tableLookup (casKey @Int 1) t) (Just 1) + assertIO (tableLookup (casKey @Int 2) t) Nothing + assertIO (tableLookupBatch [casKey @Int 1, casKey @Int 2] t) [Just 1, Nothing] - casInsert t 1 + casInsert 1 t assertCasEntries t [1] - traverse_ @[] (casInsert t) [2, 2, 2] + traverse_ @[] (`casInsert` t) [2, 2, 2] assertCasEntries t [1, 2] - traverse_ @[] (casInsert t) [1,2,3,4] + traverse_ @[] (`casInsert` t) [1, 2, 3, 4] assertCasEntries t [1, 2, 3, 4] - casDelete t $ casKey @Int 5 + casDelete (casKey @Int 5) t assertCasEntries t [1, 2, 3, 4] - traverse_ @[] (casDelete t) $ casKey @Int <$> [1, 3, 1] + traverse_ @[] (`casDelete` t) $ [1, 3, 1] assertCasEntries t [2, 4] - traverse_ @[] (casDelete t) $ casKey @Int <$> [2, 4] + traverse_ @[] (`casDelete` t) $ [2, 4] assertEmptyTable t where t = intTable db tableName @@ -283,7 +293,7 @@ casTests db tableName = do assertCasEntries :: HasCallStack => RocksDbTable Int Int -> [Int] -> IO () assertCasEntries t l = do assertNoThunks t - assertEntries t $ (\x -> (casKey x, x)) <$> l + assertEntries t [(casKey v, v) | v <- l] -- -------------------------------------------------------------------------- -- -- Main @@ -292,8 +302,7 @@ main :: IO () main = withTempRocksDb "testDb" $ \db -> do mapConcurrently_ (\i -> tableTests db $ "testTable" <> B8.pack (show i)) - ([0..100] :: [Int]) + ([0 .. 100] :: [Int]) tableBatchTests db "testTable" casTests db "testTable" casBatchTests db "testTable" - From beca678e15b4aca1a4851e018b4924c99288337d Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Tue, 27 Sep 2022 15:28:05 -0400 Subject: [PATCH 04/17] Extra eq constraints for earlier GHC --- .../chainweb-storage/src/Chainweb/Storage/Table/HashMap.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table/HashMap.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table/HashMap.hs index 1310c1959..4e50b3852 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table/HashMap.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table/HashMap.hs @@ -37,12 +37,12 @@ import Chainweb.Storage.Table -- data HashMapTable k v = HashMapTable !(TVar (HM.HashMap k v)) -instance Hashable k => ReadableTable (HashMapTable k v) k v where +instance (Hashable k, Eq k) => ReadableTable (HashMapTable k v) k v where tableLookup k (HashMapTable var) = HM.lookup k <$!> readTVarIO var tableMember k (HashMapTable var) = HM.member k <$> readTVarIO var -instance Hashable k => Table (HashMapTable k v) k v where +instance (Hashable k, Eq k) => Table (HashMapTable k v) k v where tableInsert k v (HashMapTable var) = atomically $ modifyTVar' var (HM.insert k v) tableDelete k (HashMapTable var) = @@ -50,7 +50,7 @@ instance Hashable k => Table (HashMapTable k v) k v where -- | Create new empty CAS -- -emptyTable :: Hashable k => IO (HashMapTable k v) +emptyTable :: (Hashable k, Eq k) => IO (HashMapTable k v) emptyTable = HashMapTable <$> newTVarIO mempty -- | Return all entries of CAS as List From 37f55407f79a89805dbcbcf1bb9fdd13a9c3479b Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Tue, 27 Sep 2022 15:39:18 -0400 Subject: [PATCH 05/17] Relax tableLookupM constraint to ReadableTable --- vendored/chainweb-storage/src/Chainweb/Storage/Table.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs index b5ad5a14a..90babea96 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs @@ -122,7 +122,7 @@ type CasIterator i v = Iterator i (CasKeyType v) v -- | Lookup a value by its key in a content-addressable store and throw an -- 'TableException' if the value doesn't exist in the store -tableLookupM :: (HasCallStack, Table t k v) => k -> t -> IO v +tableLookupM :: (HasCallStack, ReadableTable t k v) => k -> t -> IO v tableLookupM cas k = tableLookup cas k >>= \case Nothing -> From 7a874aece38366d038c80f5678d40cf18159b718 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 28 Sep 2022 08:06:47 -0400 Subject: [PATCH 06/17] Add Casify newtype --- .../src/Chainweb/Storage/Table.hs | 58 +++++++++++++------ 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs index 90babea96..7c86f3888 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs @@ -2,11 +2,16 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE GADTs #-} +{-# LANGUAGE InstanceSigs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE UndecidableInstances #-} -- | -- Module: Chainweb.Storage.Table @@ -18,29 +23,31 @@ -- -- API for Key-Value Stores module Chainweb.Storage.Table - ( IsCasValue (..), - ReadableTable (..), - ReadableCas, - Table (..), - Cas, - casInsert, - casInsertBatch, - casDelete, - casDeleteBatch, - IterableTable (..), - IterableCas, - Entry (..), - Iterator (..), - CasIterator, - tableLookupM, - casLookupM, - TableException (..), + ( IsCasValue (..) + , ReadableTable (..) + , ReadableCas + , Table (..) + , Casify(..) + , Cas + , casInsert + , casInsertBatch + , casDelete + , casDeleteBatch + , IterableTable (..) + , IterableCas + , Entry (..) + , Iterator (..) + , CasIterator + , tableLookupM + , casLookupM + , TableException (..) ) where import Control.Exception (Exception, SomeException) import Control.Monad.Catch (throwM) +import Data.Coerce import Data.Foldable import Data.Maybe import Data.Text (Text) @@ -83,6 +90,21 @@ class ReadableTable t k v => Table t k v | t -> k v where type Cas t v = Table t (CasKeyType v) v +newtype Casify t v = Casify (t (CasKeyType v) v) +instance forall t k v. (CasKeyType v ~ k, ReadableTable (t k v) k v) => ReadableTable (Casify t v) k v where + tableLookup = coerce @(k -> t k v -> IO (Maybe v)) tableLookup + tableLookupBatch = coerce @([k] -> t k v -> IO [Maybe v]) tableLookupBatch + tableMember = coerce @(k -> t k v -> IO Bool) tableMember +instance forall t k v. (CasKeyType v ~ k, Table (t k v) k v) => Table (Casify t v) k v where + tableInsert = coerce @(k -> v -> t k v -> IO ()) tableInsert + tableInsertBatch = coerce @([(k, v)] -> t k v -> IO ()) tableInsertBatch + tableDelete = coerce @(k -> t k v -> IO ()) tableDelete + tableDeleteBatch = coerce @([k] -> t k v -> IO ()) tableDeleteBatch +-- TODO: why is this Iterator superclass needed? +instance forall t i k v. (CasKeyType v ~ k, IterableTable (t k v) i k v, Iterator i k v) => IterableTable (Casify t v) i k v where + withTableIterator :: forall a. Casify t v -> (i -> IO a) -> IO a + withTableIterator = coerce @(t k v -> (i -> IO a) -> IO a) withTableIterator + casInsert :: (IsCasValue v, Cas t v) => v -> t -> IO () casInsert v = tableInsert (casKey v) v @@ -95,7 +117,7 @@ casDelete = tableDelete . casKey casDeleteBatch :: (IsCasValue v, Cas t v) => [v] -> t -> IO () casDeleteBatch = tableDeleteBatch . fmap casKey -class (Table t k v, Iterator i k v) => IterableTable t i k v | t -> k v, i -> k v, t -> i where +class (Table t k v, Iterator i k v) => IterableTable t i k v | t -> i k v where -- the created iterator must be positioned at the start of the table. withTableIterator :: t -> (i -> IO a) -> IO a From 119fccaff0723bfa2a4109ac9c464fbfef3a7672 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 28 Sep 2022 09:35:50 -0400 Subject: [PATCH 07/17] Make migration easier --- .../src/Chainweb/Storage/DedupStore.hs | 14 +-- .../src/Chainweb/Storage/Table.hs | 55 ++++++------ .../src/Chainweb/Storage/Table/HashMap.hs | 10 +-- .../src/Chainweb/Storage/Table/RocksDB.hs | 30 +++---- vendored/chainweb-storage/test/Main.hs | 86 +++++++++---------- 5 files changed, 98 insertions(+), 97 deletions(-) diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/DedupStore.hs b/vendored/chainweb-storage/src/Chainweb/Storage/DedupStore.hs index fb594027f..e2eab4a69 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/DedupStore.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/DedupStore.hs @@ -156,7 +156,7 @@ dedupInsert :: DedupStore k v -> k -> v -> IO () dedupInsert store k v = do dedupKey <- dedupStore (_dedupChunks store) $ BL.fromStrict $ _codecEncode (_dedupValueCodec store) v - tableInsert k dedupKey (_dedupRoots store) + tableInsert (_dedupRoots store) k dedupKey {-# INLINE dedupInsert #-} -- | @dedupLookup db k@ returns 'Just' the value at key @k@ in the @@ -165,7 +165,7 @@ dedupInsert store k v = do -- dedupLookup :: DedupStore k v -> k -> IO (Maybe v) dedupLookup store k = do - tableLookup k (_dedupRoots store) >>= \case + tableLookup (_dedupRoots store) k >>= \case Nothing -> return Nothing Just dedupKey -> do dedupRestore (_dedupChunks store) dedupKey >>= \case @@ -246,10 +246,10 @@ dedupStore' store = go0 hashAndStore :: Word8 -> B.ByteString -> IO (Int, Int, DedupHash) hashAndStore tag c = do let h = DedupHash $ BA.convert $ C.hash @_ @DedupHashAlg (B.cons tag c) - (hit, miss) <- tableMember h store >>= \x -> if x + (hit, miss) <- tableMember store h >>= \x -> if x then return (1, 0) else do - casInsert (Chunk tag h c) store + casInsert store (Chunk tag h c) return (0, 1) return (hit, miss, h) {-# INLINE hashAndStore #-} @@ -276,7 +276,7 @@ dedupRestore -> DedupHash -> IO (Maybe BL.ByteString) dedupRestore store key = fmap BB.toLazyByteString <$> do - tableLookup key store >>= \case + tableLookup store key >>= \case Nothing -> return Nothing Just (Chunk 0x0 _ b) -> return $ Just $ BB.byteString b Just (Chunk 0x1 _ b) -> Just <$> splitHashes b @@ -284,7 +284,7 @@ dedupRestore store key = fmap BB.toLazyByteString <$> do where go h = do - tableLookup h store >>= \case + tableLookup store h >>= \case Nothing -> throwM $ DedupStoreCorruptedData "Missing chunk from store" Just (Chunk 0x0 _ b) -> return (BB.byteString b) Just (Chunk 0x1 _ b) -> splitHashes b @@ -311,7 +311,7 @@ roll :: BL.ByteString -> BL.ByteString roll z = BL.fromChunks $ go seed 0 z za ze where z' = BL.unpack z - za = (BL.unpack $ BL.replicate window 0) <> z' + za = BL.unpack (BL.replicate window 0) <> z' {-# SCC za #-} ze = z' {-# SCC ze #-} diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs index 7c86f3888..80241199c 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs @@ -73,49 +73,50 @@ class Eq (CasKeyType v) => IsCasValue v where -- is either available or not available. There is no dispute about the value -- itself. class ReadableTable t k v | t -> k v where - tableLookup :: k -> t -> IO (Maybe v) - tableLookupBatch :: [k] -> t -> IO [Maybe v] - tableLookupBatch ks t = traverse (flip tableLookup t) ks - tableMember :: k -> t -> IO Bool + tableLookup :: t -> k -> IO (Maybe v) + tableLookupBatch :: t -> [k] -> IO [Maybe v] + tableLookupBatch t ks = traverse (tableLookup t) ks + tableMember :: t -> k -> IO Bool + tableMember t k = isJust <$> tableLookup t k type ReadableCas t v = ReadableTable t (CasKeyType v) v class ReadableTable t k v => Table t k v | t -> k v where - tableInsert :: k -> v -> t -> IO () - tableInsertBatch :: [(k, v)] -> t -> IO () - tableInsertBatch kvs t = traverse_ (flip (uncurry tableInsert) t) kvs - tableDelete :: k -> t -> IO () - tableDeleteBatch :: [k] -> t -> IO () - tableDeleteBatch ks t = traverse_ (flip tableDelete t) ks + tableInsert :: t -> k -> v -> IO () + tableInsertBatch :: t -> [(k, v)] -> IO () + tableInsertBatch t kvs = traverse_ (uncurry (tableInsert t)) kvs + tableDelete :: t -> k -> IO () + tableDeleteBatch :: t -> [k] -> IO () + tableDeleteBatch t ks = traverse_ (tableDelete t) ks type Cas t v = Table t (CasKeyType v) v newtype Casify t v = Casify (t (CasKeyType v) v) instance forall t k v. (CasKeyType v ~ k, ReadableTable (t k v) k v) => ReadableTable (Casify t v) k v where - tableLookup = coerce @(k -> t k v -> IO (Maybe v)) tableLookup - tableLookupBatch = coerce @([k] -> t k v -> IO [Maybe v]) tableLookupBatch - tableMember = coerce @(k -> t k v -> IO Bool) tableMember + tableLookup = coerce @(t k v -> k -> IO (Maybe v)) tableLookup + tableLookupBatch = coerce @(t k v -> [k] -> IO [Maybe v]) tableLookupBatch + tableMember = coerce @(t k v -> k -> IO Bool) tableMember instance forall t k v. (CasKeyType v ~ k, Table (t k v) k v) => Table (Casify t v) k v where - tableInsert = coerce @(k -> v -> t k v -> IO ()) tableInsert - tableInsertBatch = coerce @([(k, v)] -> t k v -> IO ()) tableInsertBatch - tableDelete = coerce @(k -> t k v -> IO ()) tableDelete - tableDeleteBatch = coerce @([k] -> t k v -> IO ()) tableDeleteBatch + tableInsert = coerce @(t k v -> k -> v -> IO ()) tableInsert + tableInsertBatch = coerce @(t k v -> [(k, v)] -> IO ()) tableInsertBatch + tableDelete = coerce @(t k v -> k -> IO ()) tableDelete + tableDeleteBatch = coerce @(t k v -> [k] -> IO ()) tableDeleteBatch -- TODO: why is this Iterator superclass needed? instance forall t i k v. (CasKeyType v ~ k, IterableTable (t k v) i k v, Iterator i k v) => IterableTable (Casify t v) i k v where withTableIterator :: forall a. Casify t v -> (i -> IO a) -> IO a withTableIterator = coerce @(t k v -> (i -> IO a) -> IO a) withTableIterator -casInsert :: (IsCasValue v, Cas t v) => v -> t -> IO () -casInsert v = tableInsert (casKey v) v +casInsert :: (IsCasValue v, Cas t v) => t -> v -> IO () +casInsert t v = tableInsert t (casKey v) v -casInsertBatch :: (IsCasValue v, Cas t v) => [v] -> t -> IO () -casInsertBatch vs = tableInsertBatch [(casKey v, v) | v <- vs] +casInsertBatch :: (IsCasValue v, Cas t v) => t -> [v] -> IO () +casInsertBatch t vs = tableInsertBatch t [(casKey v, v) | v <- vs] -casDelete :: (IsCasValue v, Cas t v) => v -> t -> IO () -casDelete = tableDelete . casKey +casDelete :: (IsCasValue v, Cas t v) => t -> v -> IO () +casDelete t = tableDelete t . casKey -casDeleteBatch :: (IsCasValue v, Cas t v) => [v] -> t -> IO () -casDeleteBatch = tableDeleteBatch . fmap casKey +casDeleteBatch :: (IsCasValue v, Cas t v) => t -> [v] -> IO () +casDeleteBatch t = tableDeleteBatch t . fmap casKey class (Table t k v, Iterator i k v) => IterableTable t i k v | t -> i k v where -- the created iterator must be positioned at the start of the table. @@ -144,7 +145,7 @@ type CasIterator i v = Iterator i (CasKeyType v) v -- | Lookup a value by its key in a content-addressable store and throw an -- 'TableException' if the value doesn't exist in the store -tableLookupM :: (HasCallStack, ReadableTable t k v) => k -> t -> IO v +tableLookupM :: (HasCallStack, ReadableTable t k v) => t -> k -> IO v tableLookupM cas k = tableLookup cas k >>= \case Nothing -> @@ -152,7 +153,7 @@ tableLookupM cas k = "tableLookupM: lookup failed for table key" Just v -> return $! v -casLookupM :: (HasCallStack, Cas t v) => CasKeyType v -> t -> IO v +casLookupM :: (HasCallStack, Cas t v) => t -> CasKeyType v -> IO v casLookupM = tableLookupM -- | Exceptions that are thrown by instances of 'IsCas'. diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table/HashMap.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table/HashMap.hs index 4e50b3852..62565979c 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table/HashMap.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table/HashMap.hs @@ -35,17 +35,17 @@ import Chainweb.Storage.Table -- | An 'IsTable' implementation that is base on 'HM.HashMap'. -- -data HashMapTable k v = HashMapTable !(TVar (HM.HashMap k v)) +newtype HashMapTable k v = HashMapTable (TVar (HM.HashMap k v)) instance (Hashable k, Eq k) => ReadableTable (HashMapTable k v) k v where - tableLookup k (HashMapTable var) = HM.lookup k <$!> readTVarIO var - tableMember k (HashMapTable var) = + tableLookup (HashMapTable var) k = HM.lookup k <$!> readTVarIO var + tableMember (HashMapTable var) k = HM.member k <$> readTVarIO var instance (Hashable k, Eq k) => Table (HashMapTable k v) k v where - tableInsert k v (HashMapTable var) = + tableInsert (HashMapTable var) k v = atomically $ modifyTVar' var (HM.insert k v) - tableDelete k (HashMapTable var) = + tableDelete (HashMapTable var) k = atomically $ modifyTVar' var (HM.delete k) -- | Create new empty CAS diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs index 8561609bc..0652f5627 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs @@ -536,18 +536,18 @@ tableMinEntry = flip withTableIterator $ \i -> iterFirst i *> iterEntry i -- instance of 'HasCasLookup'. -- instance ReadableTable (RocksDbTable k v) k v where - tableLookup k db = do + tableLookup db k = do maybeBytes <- get (_rocksDbTableDb db) mempty (encKey db k) traverse (decVal db) maybeBytes - tableMember k db = + tableMember db k = isJust <$> get (_rocksDbTableDb db) mempty (encKey db k) -- | @tableLookupBatch db ks@ returns for each @k@ in @ks@ 'Just' the value at -- key @k@ in the 'RocksDbTable' @db@ if it exists, or 'Nothing' if the @k@ -- doesn't exist in the table. -- - tableLookupBatch ks db = do + tableLookupBatch db ks = do results <- V.toList <$> multiGet (_rocksDbTableDb db) mempty (V.fromList $ map (encKey db) ks) forM results $ \case Left e -> error $ "Chainweb.Storage.Table.RocksDB.tableLookupBatch: " <> e @@ -557,21 +557,21 @@ instance ReadableTable (RocksDbTable k v) k v where -- instance of 'IsCas'. -- instance Table (RocksDbTable k v) k v where - tableInsert k v db = R.put + tableInsert db k v = R.put (_rocksDbTableDb db) R.defaultWriteOptions (encKey db k) (encVal db v) - tableDelete k db = R.delete + tableDelete db k = R.delete (_rocksDbTableDb db) R.defaultWriteOptions (encKey db k) - tableInsertBatch kvs db = + tableInsertBatch db kvs = updateBatch (uncurry (RocksDbInsert db) <$> kvs) - tableDeleteBatch ks db = + tableDeleteBatch db ks = updateBatch (RocksDbDelete db <$> ks) -- | A newtype wrapper that takes only a single type constructor. This useful in @@ -583,15 +583,15 @@ newtype RocksDbCas v = RocksDbCas { _getRocksDbCas :: RocksDbTable (CasKeyType v deriving newtype (NoThunks) instance (k ~ CasKeyType v, IsCasValue v) => ReadableTable (RocksDbCas v) k v where - tableLookup k (RocksDbCas x) = tableLookup k x - tableLookupBatch ks (RocksDbCas x) = tableLookupBatch ks x - tableMember k (RocksDbCas x) = tableMember k x + tableLookup (RocksDbCas x) k = tableLookup x k + tableLookupBatch (RocksDbCas x) ks = tableLookupBatch x ks + tableMember (RocksDbCas x) k = tableMember x k instance (k ~ CasKeyType v, IsCasValue v) => Table (RocksDbCas v) k v where - tableInsert k v (RocksDbCas x) = tableInsert k v x - tableDelete k (RocksDbCas x) = tableDelete k x - tableInsertBatch kvs (RocksDbCas x) = tableInsertBatch kvs x - tableDeleteBatch ks (RocksDbCas x) = tableDeleteBatch ks x + tableInsert (RocksDbCas x) k v = tableInsert x k v + tableDelete (RocksDbCas x) k = tableDelete x k + tableInsertBatch (RocksDbCas x) kvs = tableInsertBatch x kvs + tableDeleteBatch (RocksDbCas x) ks = tableDeleteBatch x ks -- | Create a new 'RocksDbCas'. -- @@ -610,7 +610,7 @@ newCas db vc kc n = RocksDbCas $ newTable db vc kc n -- | Excpeptions that can be thrown by functions in this module. -- data RocksDbException - = RocksDbTableIterInvalidKeyNamespace (Expected B.ByteString) (Actual B.ByteString) + = RocksDbTableIterInvalidKeyNamespace !(Expected B.ByteString) !(Actual B.ByteString) deriving (Eq, Ord, Generic, NoThunks) instance Exception RocksDbException where diff --git a/vendored/chainweb-storage/test/Main.hs b/vendored/chainweb-storage/test/Main.hs index 8c9e73118..0e3586069 100644 --- a/vendored/chainweb-storage/test/Main.hs +++ b/vendored/chainweb-storage/test/Main.hs @@ -95,14 +95,14 @@ intTable db tableName = newTable db intCodec intCodec [tableName] assertEmptyTable :: HasCallStack => RocksDbTable Int Int -> IO () assertEmptyTable t = do assertNoThunks t - assertIO (tableLookup 1 t) Nothing + assertIO (tableLookup t 1) Nothing assertEntries t [] assertEntries :: HasCallStack => RocksDbTable Int Int -> [(Int, Int)] -> IO () assertEntries t l_ = do assertNoThunks t - forM_ l $ \(k, v) -> assertIO (tableLookup k t) (Just v) - assertIO (tableLookupBatch ks t) (Just <$> vs) + forM_ l $ \(k, v) -> assertIO (tableLookup t k) (Just v) + assertIO (tableLookupBatch t ks) (Just <$> vs) assertIO (tableMinKey t) (firstOf (folded . _1) l) assertIO (tableMinValue t) (firstOf (folded . _2) l) @@ -138,19 +138,19 @@ tableTests db tableName = do assertNoThunks t assertEmptyTable t - tableInsert 1 8 t + tableInsert t 1 8 assertEntries t [(1, 8)] - tableInsert 2 9 t + tableInsert t 2 9 assertEntries t [(1, 8), (2, 9)] - tableDelete 1 t + tableDelete t 1 assertEntries t [(2, 9)] - tableInsert 2 8 t + tableInsert t 2 8 assertEntries t [(2, 8)] - tableDelete 2 t + tableDelete t 2 assertEmptyTable t where !t = intTable db tableName @@ -205,37 +205,37 @@ casBatchTests :: HasCallStack => RocksDb -> B8.ByteString -> IO () casBatchTests db tableName = do assertEmptyTable t - tableInsertBatch mempty t + tableInsertBatch t mempty assertEmptyTable t - casInsertBatch [1] t + casInsertBatch t [1] assertCasEntries t [1] - casInsertBatch [2] t + casInsertBatch t [2] assertCasEntries t [1, 2] - casDeleteBatch [2] t + casDeleteBatch t [2] assertCasEntries t [1] - casInsertBatch [1] t + casInsertBatch t [1] assertCasEntries t [1] - casInsertBatch [2, 2, 2] t + casInsertBatch t [2, 2, 2] assertCasEntries t [1, 2] - casInsertBatch [1, 2, 3, 4] t + casInsertBatch t [1, 2, 3, 4] assertCasEntries t [1, 2, 3, 4] - casDeleteBatch [5] t + casDeleteBatch t [5] assertCasEntries t [1, 2, 3, 4] - casDeleteBatch [1, 3, 1] t + casDeleteBatch t [1, 3, 1] assertCasEntries t [2, 4] - casDeleteBatch [] t + casDeleteBatch t [] assertCasEntries t [2, 4] - casDeleteBatch [2, 4] t + casDeleteBatch t [2, 4] assertEmptyTable t where t = intTable db tableName @@ -243,49 +243,49 @@ casBatchTests db tableName = do casTests :: HasCallStack => RocksDb -> B8.ByteString -> IO () casTests db tableName = do assertEmptyTable t - assertIO (tableMember 1 t) False - assertIO (tableLookup 1 t) Nothing + assertIO (tableMember t 1) False + assertIO (tableLookup t 1) Nothing - casInsertBatch mempty t + casInsertBatch t mempty assertEmptyTable t - casInsert 1 t + casInsert t 1 assertCasEntries t [1] - assertIO (tableMember (casKey @Int 1) t) True - assertIO (tableLookup (casKey @Int 1) t) (Just 1) + assertIO (tableMember t (casKey @Int 1)) True + assertIO (tableLookup t (casKey @Int 1)) (Just 1) - casInsert 2 t + casInsert t 2 assertCasEntries t [1, 2] - assertIO (tableMember (casKey @Int 1) t) True - assertIO (tableMember (casKey @Int 2) t) True - assertIO (tableLookup (casKey @Int 1) t) (Just 1) - assertIO (tableLookup (casKey @Int 2) t) (Just 2) - assertIO (tableLookupBatch [casKey @Int 1, casKey @Int 2] t) [Just 1, Just 2] + assertIO (tableMember t (casKey @Int 1)) True + assertIO (tableMember t (casKey @Int 2)) True + assertIO (tableLookup t (casKey @Int 1)) (Just 1) + assertIO (tableLookup t (casKey @Int 2)) (Just 2) + assertIO (tableLookupBatch t [casKey @Int 1, casKey @Int 2]) [Just 1, Just 2] - casDelete 2 t + casDelete t 2 assertCasEntries t [1] - assertIO (tableMember (casKey @Int 1) t) True - assertIO (tableMember (casKey @Int 2) t) False - assertIO (tableLookup (casKey @Int 1) t) (Just 1) - assertIO (tableLookup (casKey @Int 2) t) Nothing - assertIO (tableLookupBatch [casKey @Int 1, casKey @Int 2] t) [Just 1, Nothing] + assertIO (tableMember t (casKey @Int 1)) True + assertIO (tableMember t (casKey @Int 2)) False + assertIO (tableLookup t (casKey @Int 1)) (Just 1) + assertIO (tableLookup t (casKey @Int 2)) Nothing + assertIO (tableLookupBatch t [casKey @Int 1, casKey @Int 2]) [Just 1, Nothing] - casInsert 1 t + casInsert t 1 assertCasEntries t [1] - traverse_ @[] (`casInsert` t) [2, 2, 2] + traverse_ @[] (casInsert t) [2, 2, 2] assertCasEntries t [1, 2] - traverse_ @[] (`casInsert` t) [1, 2, 3, 4] + traverse_ @[] (casInsert t) [1, 2, 3, 4] assertCasEntries t [1, 2, 3, 4] - casDelete (casKey @Int 5) t + casDelete t (casKey @Int 5) assertCasEntries t [1, 2, 3, 4] - traverse_ @[] (`casDelete` t) $ [1, 3, 1] + traverse_ @[] (casDelete t) [1, 3, 1] assertCasEntries t [2, 4] - traverse_ @[] (`casDelete` t) $ [2, 4] + traverse_ @[] (casDelete t) [2, 4] assertEmptyTable t where t = intTable db tableName From 62798865d6a7a9c1780f9a0cdcf2d817dc461214 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 28 Sep 2022 10:20:00 -0400 Subject: [PATCH 08/17] Add *1 type aliases --- .../src/Chainweb/Storage/Table.hs | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs index 80241199c..6329fc0ad 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs @@ -9,6 +9,8 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE QuantifiedConstraints #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} @@ -25,8 +27,10 @@ module Chainweb.Storage.Table ( IsCasValue (..) , ReadableTable (..) + , ReadableTable1 , ReadableCas , Table (..) + , Table1 , Casify(..) , Cas , casInsert @@ -34,9 +38,11 @@ module Chainweb.Storage.Table , casDelete , casDeleteBatch , IterableTable (..) + , IterableTable1 , IterableCas , Entry (..) , Iterator (..) + , Iterator1 , CasIterator , tableLookupM , casLookupM @@ -80,6 +86,7 @@ class ReadableTable t k v | t -> k v where tableMember t k = isJust <$> tableLookup t k type ReadableCas t v = ReadableTable t (CasKeyType v) v +type ReadableTable1 t = forall k v. ReadableTable (t k v) k v class ReadableTable t k v => Table t k v | t -> k v where tableInsert :: t -> k -> v -> IO () @@ -88,24 +95,10 @@ class ReadableTable t k v => Table t k v | t -> k v where tableDelete :: t -> k -> IO () tableDeleteBatch :: t -> [k] -> IO () tableDeleteBatch t ks = traverse_ (tableDelete t) ks +type Table1 t = forall k v. Table (t k v) k v type Cas t v = Table t (CasKeyType v) v -newtype Casify t v = Casify (t (CasKeyType v) v) -instance forall t k v. (CasKeyType v ~ k, ReadableTable (t k v) k v) => ReadableTable (Casify t v) k v where - tableLookup = coerce @(t k v -> k -> IO (Maybe v)) tableLookup - tableLookupBatch = coerce @(t k v -> [k] -> IO [Maybe v]) tableLookupBatch - tableMember = coerce @(t k v -> k -> IO Bool) tableMember -instance forall t k v. (CasKeyType v ~ k, Table (t k v) k v) => Table (Casify t v) k v where - tableInsert = coerce @(t k v -> k -> v -> IO ()) tableInsert - tableInsertBatch = coerce @(t k v -> [(k, v)] -> IO ()) tableInsertBatch - tableDelete = coerce @(t k v -> k -> IO ()) tableDelete - tableDeleteBatch = coerce @(t k v -> [k] -> IO ()) tableDeleteBatch --- TODO: why is this Iterator superclass needed? -instance forall t i k v. (CasKeyType v ~ k, IterableTable (t k v) i k v, Iterator i k v) => IterableTable (Casify t v) i k v where - withTableIterator :: forall a. Casify t v -> (i -> IO a) -> IO a - withTableIterator = coerce @(t k v -> (i -> IO a) -> IO a) withTableIterator - casInsert :: (IsCasValue v, Cas t v) => t -> v -> IO () casInsert t v = tableInsert t (casKey v) v @@ -121,6 +114,7 @@ casDeleteBatch t = tableDeleteBatch t . fmap casKey class (Table t k v, Iterator i k v) => IterableTable t i k v | t -> i k v where -- the created iterator must be positioned at the start of the table. withTableIterator :: t -> (i -> IO a) -> IO a +type IterableTable1 t i = forall k v. IterableTable (t k v) (i k v) k v type IterableCas t v = IterableTable t (CasKeyType v) v @@ -140,9 +134,26 @@ class Iterator i k v | i -> k v where iterValue i = (fmap . fmap) (\(Entry _ v) -> v) $ iterEntry i iterValid :: i -> IO Bool iterValid i = isJust <$> iterKey i +type Iterator1 i = forall k v. Iterator (i k v) k v type CasIterator i v = Iterator i (CasKeyType v) v +newtype Casify t v = Casify (t (CasKeyType v) v) +instance forall t k v. (CasKeyType v ~ k, ReadableTable (t k v) k v) => ReadableTable (Casify t v) k v where + tableLookup = coerce @(t k v -> k -> IO (Maybe v)) tableLookup + tableLookupBatch = coerce @(t k v -> [k] -> IO [Maybe v]) tableLookupBatch + tableMember = coerce @(t k v -> k -> IO Bool) tableMember +instance forall t k v. (CasKeyType v ~ k, Table (t k v) k v) => Table (Casify t v) k v where + tableInsert = coerce @(t k v -> k -> v -> IO ()) tableInsert + tableInsertBatch = coerce @(t k v -> [(k, v)] -> IO ()) tableInsertBatch + tableDelete = coerce @(t k v -> k -> IO ()) tableDelete + tableDeleteBatch = coerce @(t k v -> [k] -> IO ()) tableDeleteBatch +-- TODO: why is this Iterator superclass needed? +instance forall t i k v. (CasKeyType v ~ k, IterableTable (t k v) i k v, Iterator i k v) => IterableTable (Casify t v) i k v where + withTableIterator :: forall a. Casify t v -> (i -> IO a) -> IO a + withTableIterator = coerce @(t k v -> (i -> IO a) -> IO a) withTableIterator + + -- | Lookup a value by its key in a content-addressable store and throw an -- 'TableException' if the value doesn't exist in the store tableLookupM :: (HasCallStack, ReadableTable t k v) => t -> k -> IO v From f675b1a15541399bfbbbf84500359bf61875818f Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 28 Sep 2022 12:17:44 -0400 Subject: [PATCH 09/17] Relax casLookupM constraint and fix indentation --- .../src/Chainweb/Storage/DedupStore.hs | 4 +-- .../src/Chainweb/Storage/Table.hs | 26 ++++++++------ .../src/Chainweb/Storage/Table/RocksDB.hs | 34 ------------------- 3 files changed, 17 insertions(+), 47 deletions(-) diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/DedupStore.hs b/vendored/chainweb-storage/src/Chainweb/Storage/DedupStore.hs index e2eab4a69..57991d399 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/DedupStore.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/DedupStore.hs @@ -112,7 +112,7 @@ instance Exception DedupStoreException -- data DedupStore k v = DedupStore { _dedupRoots :: !(RocksDbTable k DedupHash) - , _dedupChunks :: !(RocksDbCas Chunk) + , _dedupChunks :: !(Casify RocksDbTable Chunk) , _dedupValueCodec :: !(Codec v) } @@ -127,7 +127,7 @@ newDedupStore -> DedupStore k v newDedupStore rdb vc kc n = DedupStore { _dedupRoots = newTable rdb dedupHashCodec kc (n <> ["roots"]) - , _dedupChunks = newCas rdb dedupChunkCodec dedupHashCodec (n <> ["chunks"]) + , _dedupChunks = Casify $ newTable rdb dedupChunkCodec dedupHashCodec (n <> ["chunks"]) , _dedupValueCodec = vc } where diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs index 6329fc0ad..3af676aba 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs @@ -138,21 +138,25 @@ type Iterator1 i = forall k v. Iterator (i k v) k v type CasIterator i v = Iterator i (CasKeyType v) v +-- | A newtype wrapper that takes only a single type constructor. This useful in +-- situations where a Higher Order type constructor for a CAS is required. A +-- type synonym doesn't work in this situation because type synonyms must be +-- fully applied. +-- newtype Casify t v = Casify (t (CasKeyType v) v) instance forall t k v. (CasKeyType v ~ k, ReadableTable (t k v) k v) => ReadableTable (Casify t v) k v where - tableLookup = coerce @(t k v -> k -> IO (Maybe v)) tableLookup - tableLookupBatch = coerce @(t k v -> [k] -> IO [Maybe v]) tableLookupBatch - tableMember = coerce @(t k v -> k -> IO Bool) tableMember + tableLookup = coerce @(t k v -> k -> IO (Maybe v)) tableLookup + tableLookupBatch = coerce @(t k v -> [k] -> IO [Maybe v]) tableLookupBatch + tableMember = coerce @(t k v -> k -> IO Bool) tableMember instance forall t k v. (CasKeyType v ~ k, Table (t k v) k v) => Table (Casify t v) k v where - tableInsert = coerce @(t k v -> k -> v -> IO ()) tableInsert - tableInsertBatch = coerce @(t k v -> [(k, v)] -> IO ()) tableInsertBatch - tableDelete = coerce @(t k v -> k -> IO ()) tableDelete - tableDeleteBatch = coerce @(t k v -> [k] -> IO ()) tableDeleteBatch + tableInsert = coerce @(t k v -> k -> v -> IO ()) tableInsert + tableInsertBatch = coerce @(t k v -> [(k, v)] -> IO ()) tableInsertBatch + tableDelete = coerce @(t k v -> k -> IO ()) tableDelete + tableDeleteBatch = coerce @(t k v -> [k] -> IO ()) tableDeleteBatch -- TODO: why is this Iterator superclass needed? instance forall t i k v. (CasKeyType v ~ k, IterableTable (t k v) i k v, Iterator i k v) => IterableTable (Casify t v) i k v where - withTableIterator :: forall a. Casify t v -> (i -> IO a) -> IO a - withTableIterator = coerce @(t k v -> (i -> IO a) -> IO a) withTableIterator - + withTableIterator :: forall a. Casify t v -> (i -> IO a) -> IO a + withTableIterator = coerce @(t k v -> (i -> IO a) -> IO a) withTableIterator -- | Lookup a value by its key in a content-addressable store and throw an -- 'TableException' if the value doesn't exist in the store @@ -164,7 +168,7 @@ tableLookupM cas k = "tableLookupM: lookup failed for table key" Just v -> return $! v -casLookupM :: (HasCallStack, Cas t v) => t -> CasKeyType v -> IO v +casLookupM :: (HasCallStack, ReadableCas t v) => t -> CasKeyType v -> IO v casLookupM = tableLookupM -- | Exceptions that are thrown by instances of 'IsCas'. diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs index 0652f5627..6fcef66cc 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs @@ -79,10 +79,6 @@ module Chainweb.Storage.Table.RocksDB , tableMinValue , tableMinEntry - -- * RocksDbCas - , RocksDbCas(..) - , newCas - -- * RocksDB-specific tools , checkpointRocksDb , deleteRangeRocksDb @@ -574,36 +570,6 @@ instance Table (RocksDbTable k v) k v where tableDeleteBatch db ks = updateBatch (RocksDbDelete db <$> ks) --- | A newtype wrapper that takes only a single type constructor. This useful in --- situations where a Higher Order type constructor for a CAS is required. A --- type synonym doesn't work in this situation because type synonyms must be --- fully applied. --- -newtype RocksDbCas v = RocksDbCas { _getRocksDbCas :: RocksDbTable (CasKeyType v) v } - deriving newtype (NoThunks) - -instance (k ~ CasKeyType v, IsCasValue v) => ReadableTable (RocksDbCas v) k v where - tableLookup (RocksDbCas x) k = tableLookup x k - tableLookupBatch (RocksDbCas x) ks = tableLookupBatch x ks - tableMember (RocksDbCas x) k = tableMember x k - -instance (k ~ CasKeyType v, IsCasValue v) => Table (RocksDbCas v) k v where - tableInsert (RocksDbCas x) k v = tableInsert x k v - tableDelete (RocksDbCas x) k = tableDelete x k - tableInsertBatch (RocksDbCas x) kvs = tableInsertBatch x kvs - tableDeleteBatch (RocksDbCas x) ks = tableDeleteBatch x ks - --- | Create a new 'RocksDbCas'. --- -newCas - :: CasKeyType v ~ k - => RocksDb - -> Codec v - -> Codec k - -> [B.ByteString] - -> RocksDbCas v -newCas db vc kc n = RocksDbCas $ newTable db vc kc n - -- -------------------------------------------------------------------------- -- -- Exceptions From b136f4931ce948ef86aa11f0df1c9b9020ee686d Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 28 Sep 2022 12:28:34 -0400 Subject: [PATCH 10/17] Add unCasify accessor --- vendored/chainweb-storage/src/Chainweb/Storage/Table.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs index 3af676aba..9ae5ca7ae 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs @@ -143,7 +143,7 @@ type CasIterator i v = Iterator i (CasKeyType v) v -- type synonym doesn't work in this situation because type synonyms must be -- fully applied. -- -newtype Casify t v = Casify (t (CasKeyType v) v) +newtype Casify t v = Casify { unCasify :: t (CasKeyType v) v } instance forall t k v. (CasKeyType v ~ k, ReadableTable (t k v) k v) => ReadableTable (Casify t v) k v where tableLookup = coerce @(t k v -> k -> IO (Maybe v)) tableLookup tableLookupBatch = coerce @(t k v -> [k] -> IO [Maybe v]) tableLookupBatch From 438bcc411da3b41e7a219800e24b7fc4b3525cf0 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Sat, 15 Oct 2022 18:25:53 -0400 Subject: [PATCH 11/17] Eachify tableLookupBatch --- vendored/chainweb-storage/src/Chainweb/Storage/Table.hs | 8 +++++--- .../src/Chainweb/Storage/Table/RocksDB.hs | 3 ++- vendored/chainweb-storage/test/Main.hs | 1 - 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs index 9ae5ca7ae..10b825cb5 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs @@ -51,6 +51,7 @@ module Chainweb.Storage.Table where import Control.Exception (Exception, SomeException) +import Control.Lens import Control.Monad.Catch (throwM) import Data.Coerce @@ -80,8 +81,8 @@ class Eq (CasKeyType v) => IsCasValue v where -- itself. class ReadableTable t k v | t -> k v where tableLookup :: t -> k -> IO (Maybe v) - tableLookupBatch :: t -> [k] -> IO [Maybe v] - tableLookupBatch t ks = traverse (tableLookup t) ks + tableLookupBatch :: Each s t' k (Maybe v) => t -> s -> IO t' + tableLookupBatch t ks = each (tableLookup t) ks tableMember :: t -> k -> IO Bool tableMember t k = isJust <$> tableLookup t k @@ -146,7 +147,8 @@ type CasIterator i v = Iterator i (CasKeyType v) v newtype Casify t v = Casify { unCasify :: t (CasKeyType v) v } instance forall t k v. (CasKeyType v ~ k, ReadableTable (t k v) k v) => ReadableTable (Casify t v) k v where tableLookup = coerce @(t k v -> k -> IO (Maybe v)) tableLookup - tableLookupBatch = coerce @(t k v -> [k] -> IO [Maybe v]) tableLookupBatch + tableLookupBatch :: forall s t'. Each s t' k (Maybe v) => Casify t v -> s -> IO t' + tableLookupBatch = coerce @(t k v -> s -> IO t') tableLookupBatch tableMember = coerce @(t k v -> k -> IO Bool) tableMember instance forall t k v. (CasKeyType v ~ k, Table (t k v) k v) => Table (Casify t v) k v where tableInsert = coerce @(t k v -> k -> v -> IO ()) tableInsert diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs index 6fcef66cc..fa6ffa142 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs @@ -86,6 +86,7 @@ module Chainweb.Storage.Table.RocksDB ) where import Control.Exception(evaluate) +import Control.Lens import Control.Monad import Control.Monad.Catch import Control.Monad.IO.Class @@ -543,7 +544,7 @@ instance ReadableTable (RocksDbTable k v) k v where -- key @k@ in the 'RocksDbTable' @db@ if it exists, or 'Nothing' if the @k@ -- doesn't exist in the table. -- - tableLookupBatch db ks = do + tableLookupBatch db = unsafePartsOf each $ \ks -> do results <- V.toList <$> multiGet (_rocksDbTableDb db) mempty (V.fromList $ map (encKey db) ks) forM results $ \case Left e -> error $ "Chainweb.Storage.Table.RocksDB.tableLookupBatch: " <> e diff --git a/vendored/chainweb-storage/test/Main.hs b/vendored/chainweb-storage/test/Main.hs index 0e3586069..eeb536b2d 100644 --- a/vendored/chainweb-storage/test/Main.hs +++ b/vendored/chainweb-storage/test/Main.hs @@ -1,5 +1,4 @@ {-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} From 55ec5d676480084fc7363da155b241eb8430d7f2 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Sun, 16 Oct 2022 13:53:01 -0400 Subject: [PATCH 12/17] Explicitly use c_rocksdb_free instead of our own free, traversalify the bazaar code --- .../chainweb-storage/chainweb-storage.cabal | 1 + .../src/Chainweb/Storage/Table.hs | 12 ++++--- .../src/Chainweb/Storage/Table/RocksDB.hs | 31 ++++++++++--------- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/vendored/chainweb-storage/chainweb-storage.cabal b/vendored/chainweb-storage/chainweb-storage.cabal index bef9c4aa3..d3901564b 100644 --- a/vendored/chainweb-storage/chainweb-storage.cabal +++ b/vendored/chainweb-storage/chainweb-storage.cabal @@ -48,6 +48,7 @@ library , hashable >=1.2 , lens >=4.16 , memory >=0.14 + , mtl >= 2.2 , nothunks >= 0.1.0.0 , rocksdb-haskell-kadena >=1.1.0 , stm >=2.4 diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs index 10b825cb5..21a5ffd28 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs @@ -27,6 +27,7 @@ module Chainweb.Storage.Table ( IsCasValue (..) , ReadableTable (..) + , tableLookupBatch , ReadableTable1 , ReadableCas , Table (..) @@ -81,11 +82,14 @@ class Eq (CasKeyType v) => IsCasValue v where -- itself. class ReadableTable t k v | t -> k v where tableLookup :: t -> k -> IO (Maybe v) - tableLookupBatch :: Each s t' k (Maybe v) => t -> s -> IO t' - tableLookupBatch t ks = each (tableLookup t) ks + tableLookupBatch' :: t -> Traversal s r k (Maybe v) -> s -> IO r + tableLookupBatch' t l = l (tableLookup t) tableMember :: t -> k -> IO Bool tableMember t k = isJust <$> tableLookup t k +tableLookupBatch :: (ReadableTable t k v, Each s t' k (Maybe v)) => t -> s -> IO t' +tableLookupBatch t = tableLookupBatch' t each + type ReadableCas t v = ReadableTable t (CasKeyType v) v type ReadableTable1 t = forall k v. ReadableTable (t k v) k v @@ -147,8 +151,8 @@ type CasIterator i v = Iterator i (CasKeyType v) v newtype Casify t v = Casify { unCasify :: t (CasKeyType v) v } instance forall t k v. (CasKeyType v ~ k, ReadableTable (t k v) k v) => ReadableTable (Casify t v) k v where tableLookup = coerce @(t k v -> k -> IO (Maybe v)) tableLookup - tableLookupBatch :: forall s t'. Each s t' k (Maybe v) => Casify t v -> s -> IO t' - tableLookupBatch = coerce @(t k v -> s -> IO t') tableLookupBatch + -- can't seem to write `coerce` without the resulting instantiation being impredicative + tableLookupBatch' (Casify t) b = tableLookupBatch' t b tableMember = coerce @(t k v -> k -> IO Bool) tableMember instance forall t k v. (CasKeyType v ~ k, Table (t k v) k v) => Table (Casify t v) k v where tableInsert = coerce @(t k v -> k -> v -> IO ()) tableInsert diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs index fa6ffa142..d46e6b91e 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table/RocksDB.hs @@ -94,6 +94,7 @@ import Control.Monad.IO.Class import Data.ByteString(ByteString) import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as B8 +import qualified Data.ByteString.Internal as BI import qualified Data.ByteString.Unsafe as BU import Data.Coerce import Data.Foldable @@ -102,7 +103,6 @@ import Data.Maybe import Chainweb.Storage.Table import Data.String import qualified Data.Text as T -import qualified Data.Vector as V import Foreign import Foreign.C @@ -532,20 +532,20 @@ tableMinEntry = flip withTableIterator $ \i -> iterFirst i *> iterEntry i -- | For a 'IsCasValue' @v@ with 'CasKeyType v ~ k@, a 'RocksDbTable k v' is an -- instance of 'HasCasLookup'. -- -instance ReadableTable (RocksDbTable k v) k v where +instance forall k v. ReadableTable (RocksDbTable k v) k v where tableLookup db k = do - maybeBytes <- get (_rocksDbTableDb db) mempty (encKey db k) + maybeBytes <- getRocksDb (_rocksDbTableDb db) mempty (encKey db k) traverse (decVal db) maybeBytes tableMember db k = - isJust <$> get (_rocksDbTableDb db) mempty (encKey db k) + isJust <$> getRocksDb (_rocksDbTableDb db) mempty (encKey db k) -- | @tableLookupBatch db ks@ returns for each @k@ in @ks@ 'Just' the value at -- key @k@ in the 'RocksDbTable' @db@ if it exists, or 'Nothing' if the @k@ -- doesn't exist in the table. -- - tableLookupBatch db = unsafePartsOf each $ \ks -> do - results <- V.toList <$> multiGet (_rocksDbTableDb db) mempty (V.fromList $ map (encKey db) ks) + tableLookupBatch' db t = unsafePartsOf t $ \ks -> do + results <- multiGet (_rocksDbTableDb db) mempty (map (encKey db) ks) forM results $ \case Left e -> error $ "Chainweb.Storage.Table.RocksDB.tableLookupBatch: " <> e Right x -> traverse (decVal db) x @@ -687,8 +687,8 @@ compactRangeRocksDb table range = -- | Read a value by key. -- One less copy than the version in rocksdb-haskell by using unsafePackCStringFinalizer. -get :: MonadIO m => R.DB -> R.ReadOptions -> ByteString -> m (Maybe ByteString) -get (R.DB db_ptr) opts key = liftIO $ R.withReadOptions opts $ \opts_ptr -> +getRocksDb :: MonadIO m => R.DB -> R.ReadOptions -> ByteString -> m (Maybe ByteString) +getRocksDb (R.DB db_ptr) opts key = liftIO $ R.withReadOptions opts $ \opts_ptr -> BU.unsafeUseAsCStringLen key $ \(key_ptr, klen) -> alloca $ \vlen_ptr -> do val_ptr <- checked "Chainweb.Storage.Table.RocksDB.get" $ @@ -718,8 +718,8 @@ multiGet :: MonadIO m => R.DB -> R.ReadOptions - -> V.Vector ByteString - -> m (V.Vector (Either String (Maybe ByteString))) + -> [ByteString] + -> m [Either String (Maybe ByteString)] multiGet (R.DB db_ptr) opts keys = liftIO $ R.withReadOptions opts $ \opts_ptr -> allocaArray len $ \keysArray -> allocaArray len $ \keySizesArray -> @@ -737,21 +737,22 @@ multiGet (R.DB db_ptr) opts keys = liftIO $ R.withReadOptions opts $ \opts_ptr - keysArray keySizesArray valuesArray valueSizesArray errsArray - V.generateM len $ \i -> do + forM [0..len-1] $ \i -> do valuePtr <- peekElemOff valuesArray i if valuePtr /= nullPtr then do valueLen <- R.cSizeToInt <$> peekElemOff valueSizesArray i - r <- BU.unsafePackMallocCStringLen (valuePtr, valueLen) + r <- BU.unsafePackCStringFinalizer (castPtr valuePtr) valueLen (C.c_rocksdb_free valuePtr) return $ Right $ Just r else do errPtr <- peekElemOff errsArray i if errPtr /= nullPtr then do - err <- B8.unpack <$> BU.unsafePackMallocCString errPtr + errLen <- BI.c_strlen errPtr + err <- B8.unpack <$> BU.unsafePackCStringFinalizer (castPtr errPtr) (fromIntegral errLen :: Int) (C.c_rocksdb_free errPtr) return $ Left err else return $ Right Nothing - in go 0 $ V.toList keys + in go 0 keys where - len = V.length keys + len = length keys From cdf7fafa7fde6fd9bfa31f44a0e3bf63c4d7ebcc Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 4 Jan 2023 17:24:46 -0500 Subject: [PATCH 13/17] Fix docs and build --- vendored/chainweb-storage/.github/workflows/build.yml | 2 +- vendored/chainweb-storage/cabal.project | 2 +- vendored/chainweb-storage/src/Chainweb/Storage/Table.hs | 9 +++------ 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/vendored/chainweb-storage/.github/workflows/build.yml b/vendored/chainweb-storage/.github/workflows/build.yml index 53f98b1a9..432e10f78 100644 --- a/vendored/chainweb-storage/.github/workflows/build.yml +++ b/vendored/chainweb-storage/.github/workflows/build.yml @@ -36,7 +36,7 @@ jobs: run: | case "${{ runner.os }}" in Linux) - sudo apt-get install -y librocksdb-dev + sudo apt-get install -y libgflags-dev liblz4-dev libzstd-dev libsnappy-dev libbz2-dev ;; mac*) brew install rocksdb diff --git a/vendored/chainweb-storage/cabal.project b/vendored/chainweb-storage/cabal.project index b4fb0a867..b4524e733 100644 --- a/vendored/chainweb-storage/cabal.project +++ b/vendored/chainweb-storage/cabal.project @@ -3,4 +3,4 @@ packages: chainweb-storage.cabal source-repository-package type: git location: https://github.com/kadena-io/rocksdb-haskell.git - tag: 2f0bae730d137b7edee090c4014d891675933ba5 + tag: 42cfb81426fe1cadfe411a723942c55bdb54285e diff --git a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs index 21a5ffd28..484a1719d 100644 --- a/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs +++ b/vendored/chainweb-storage/src/Chainweb/Storage/Table.hs @@ -75,15 +75,12 @@ class Eq (CasKeyType v) => IsCasValue v where type CasKeyType v casKey :: v -> CasKeyType v --- | Read-Only View of a Content Addressed Key-Value Store +-- | Read-Only View of a Key-Value Store -- --- Since the key uniquely determines the content of the store a value for a key --- is either available or not available. There is no dispute about the value --- itself. class ReadableTable t k v | t -> k v where tableLookup :: t -> k -> IO (Maybe v) tableLookupBatch' :: t -> Traversal s r k (Maybe v) -> s -> IO r - tableLookupBatch' t l = l (tableLookup t) + tableLookupBatch' t l = l (tableLookup t) tableMember :: t -> k -> IO Bool tableMember t k = isJust <$> tableLookup t k @@ -164,7 +161,7 @@ instance forall t i k v. (CasKeyType v ~ k, IterableTable (t k v) i k v, Iterato withTableIterator :: forall a. Casify t v -> (i -> IO a) -> IO a withTableIterator = coerce @(t k v -> (i -> IO a) -> IO a) withTableIterator --- | Lookup a value by its key in a content-addressable store and throw an +-- | Lookup a value by its key in a key-value store and throw an -- 'TableException' if the value doesn't exist in the store tableLookupM :: (HasCallStack, ReadableTable t k v) => t -> k -> IO v tableLookupM cas k = From 8e1e139dbc8aa964f278a2d1afe77c02c57e3342 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 5 Jan 2023 12:38:26 -0500 Subject: [PATCH 14/17] Attempt mac fix --- vendored/chainweb-storage/cabal.project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendored/chainweb-storage/cabal.project b/vendored/chainweb-storage/cabal.project index b4524e733..0a731ee3c 100644 --- a/vendored/chainweb-storage/cabal.project +++ b/vendored/chainweb-storage/cabal.project @@ -3,4 +3,4 @@ packages: chainweb-storage.cabal source-repository-package type: git location: https://github.com/kadena-io/rocksdb-haskell.git - tag: 42cfb81426fe1cadfe411a723942c55bdb54285e + tag: b0ce9953106c21cf2b46e295f96b0427b702d1fe From 06e72be24870f682695f1ec4f5c28fb3c92a71c5 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 5 Jan 2023 12:41:13 -0500 Subject: [PATCH 15/17] s/8.10.5/8.10.7 --- vendored/chainweb-storage/.github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendored/chainweb-storage/.github/workflows/build.yml b/vendored/chainweb-storage/.github/workflows/build.yml index 432e10f78..5392d8b3c 100644 --- a/vendored/chainweb-storage/.github/workflows/build.yml +++ b/vendored/chainweb-storage/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - ghc: ['8.8.4', '8.10.5', '9.0.1'] + ghc: ['8.8.4', '8.10.7', '9.0.1'] cabal: ['3.4'] os: - 'ubuntu-18.04' From bfcc3e001e30fb15a4dc7e3a7386c8ddd4e65e55 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 5 Jan 2023 13:10:12 -0500 Subject: [PATCH 16/17] Include CI updates --- .../.github/workflows/build.yml | 18 +++++++++--------- .../chainweb-storage/chainweb-storage.cabal | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/vendored/chainweb-storage/.github/workflows/build.yml b/vendored/chainweb-storage/.github/workflows/build.yml index 5392d8b3c..1bced9982 100644 --- a/vendored/chainweb-storage/.github/workflows/build.yml +++ b/vendored/chainweb-storage/.github/workflows/build.yml @@ -1,6 +1,7 @@ name: Build on: + workflow_dispatch: push: schedule: - cron: '0 8 * * *' @@ -13,11 +14,11 @@ jobs: strategy: fail-fast: false matrix: - ghc: ['8.8.4', '8.10.7', '9.0.1'] - cabal: ['3.4'] + ghc: ['8.10.7', '9.0.2', '9.2'] + cabal: ['3.6'] os: - - 'ubuntu-18.04' - 'ubuntu-20.04' + - 'ubuntu-22.04' - 'macOS-latest' # - 'windows-latest' # windows builds generally work but are flaky on the github runners @@ -25,9 +26,9 @@ jobs: # Setup - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install GHC and Cabal - uses: haskell/actions/setup@v1.2.3 + uses: haskell/actions/setup@v2 with: ghc-version: ${{ matrix.ghc }} cabal-version: ${{ matrix.cabal }} @@ -54,20 +55,19 @@ jobs: documentation: True benchmarks: True tests: True - write-ghc-environment-files: always EOF # Restore Packages from Caches - - uses: actions/cache@v2.1.6 + - uses: actions/cache@v3 name: Cache ~/.cabal/packages with: path: | ~/.cabal/packages ~/.cabal/store dist-newstyle - key: ${{ matrix.os }}-${{ matrix.ghc }}-cabal-cache-${{ github.sha }} + key: ${{ matrix.os }}-${{ matrix.ghc }}-cabal-${{ github.sha }} restore-keys: | - ${{ matrix.os }}-${{ matrix.ghc }}-cabal-cache- + ${{ matrix.os }}-${{ matrix.ghc }}-cabal- # Build - name: Update package database diff --git a/vendored/chainweb-storage/chainweb-storage.cabal b/vendored/chainweb-storage/chainweb-storage.cabal index d3901564b..abce3922d 100644 --- a/vendored/chainweb-storage/chainweb-storage.cabal +++ b/vendored/chainweb-storage/chainweb-storage.cabal @@ -37,7 +37,7 @@ library Chainweb.Storage.Table.RocksDB Chainweb.Storage.DedupStore build-depends: - , base >=4.10 && <4.16 + , base >=4.10 && <5 , bytestring >=0.10 , containers >=0.5 , cryptonite >= 0.25 @@ -75,7 +75,7 @@ test-suite rocksdb-tests -- external , async >=2.2 - , base >=4.10 && <4.16 + , base >=4.10 && <5 , bytestring >=0.10 , exceptions >=0.10 , lens >=4.16 From 0eb5697572ede43885f6dd596617cc08b857ce0b Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 5 Jan 2023 13:11:38 -0500 Subject: [PATCH 17/17] Remove mac fix attempt --- vendored/chainweb-storage/cabal.project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendored/chainweb-storage/cabal.project b/vendored/chainweb-storage/cabal.project index 0a731ee3c..b4524e733 100644 --- a/vendored/chainweb-storage/cabal.project +++ b/vendored/chainweb-storage/cabal.project @@ -3,4 +3,4 @@ packages: chainweb-storage.cabal source-repository-package type: git location: https://github.com/kadena-io/rocksdb-haskell.git - tag: b0ce9953106c21cf2b46e295f96b0427b702d1fe + tag: 42cfb81426fe1cadfe411a723942c55bdb54285e