tahoe-great-black-swamp-0.3.0.1: src/TahoeLAFS/Storage/Backend/Memory.hs
module TahoeLAFS.Storage.Backend.Memory (
MemoryBackend (MemoryBackend),
memoryBackend,
) where
import Prelude hiding (
lookup,
map,
)
import Network.HTTP.Types (
ByteRanges,
)
import Control.Exception (
throwIO,
)
import Data.Maybe (fromMaybe)
import Data.IORef (
IORef,
atomicModifyIORef',
modifyIORef,
newIORef,
readIORef,
)
import Data.Map.Strict (
Map,
adjust,
filterWithKey,
fromList,
insert,
keys,
lookup,
map,
toList,
)
import qualified Data.Set as Set
import TahoeLAFS.Storage.API (
AllocateBuckets,
AllocationResult (..),
CBORSet (..),
CorruptionDetails,
Offset,
QueryRange,
ReadResult,
ReadTestWriteResult (..),
ReadTestWriteVectors (..),
ShareData,
ShareNumber,
Size,
StorageIndex,
TestWriteVectors (..),
Version (..),
Version1Parameters (..),
WriteVector (..),
shareNumbers,
)
import TahoeLAFS.Storage.Backend (
Backend (..),
ImmutableShareAlreadyWritten (ImmutableShareAlreadyWritten),
)
type ShareStorage = Map StorageIndex (Map ShareNumber ShareData)
type BucketStorage = Map StorageIndex (Map ShareNumber (Size, ShareData))
data MemoryBackend = MemoryBackend
{ immutableShares :: IORef ShareStorage -- Completely written immutable shares
, mutableShares :: IORef ShareStorage -- Completely written mutable shares
, buckets :: IORef BucketStorage -- In-progress immutable share uploads
}
instance Show MemoryBackend where
show _ = "<MemoryBackend>"
instance Backend MemoryBackend where
version backend = do
totalSize <- totalShareSize backend
return
Version
{ applicationVersion = "(memory)"
, parameters =
Version1Parameters
{ maximumImmutableShareSize = 1024 * 1024 * 64
, maximumMutableShareSize = 1024 * 1024 * 64
, availableSpace = (1024 * 1024 * 1024) - totalSize
}
}
createMutableStorageIndex :: MemoryBackend -> StorageIndex -> AllocateBuckets -> IO AllocationResult
createMutableStorageIndex _backend _storageIndex params =
return
AllocationResult
{ alreadyHave = mempty
, allocated = shareNumbers params
}
getMutableShareNumbers :: MemoryBackend -> StorageIndex -> IO (CBORSet ShareNumber)
getMutableShareNumbers backend storageIndex = do
shares' <- readIORef $ mutableShares backend
return $
CBORSet . Set.fromList $
maybe [] keys $
lookup storageIndex shares'
readvAndTestvAndWritev :: MemoryBackend -> StorageIndex -> ReadTestWriteVectors -> IO ReadTestWriteResult
readvAndTestvAndWritev
backend
storageIndex
(ReadTestWriteVectors _secrets testWritev _readv) = do
-- TODO implement readv and testv parts. implement secrets part.
let shares = mutableShares backend
modifyIORef shares $ addShares storageIndex (shares' testWritev)
return
ReadTestWriteResult
{ success = True
, readData = mempty
}
where
shares' ::
Map ShareNumber TestWriteVectors ->
[(ShareNumber, ShareData)]
shares' testWritevs =
[ (shareNumber, shareData writev)
| (shareNumber, testWritev') <- toList testWritevs
, writev <- write testWritev'
]
createImmutableStorageIndex :: MemoryBackend -> StorageIndex -> AllocateBuckets -> IO AllocationResult
createImmutableStorageIndex _backend _idx params =
return
AllocationResult
{ alreadyHave = mempty
, allocated = shareNumbers params
}
writeImmutableShare :: MemoryBackend -> StorageIndex -> ShareNumber -> ShareData -> Maybe ByteRanges -> IO ()
writeImmutableShare backend storageIndex shareNumber shareData Nothing = do
-- shares <- readIORef (immutableShares backend) -- XXX uh, is this right?!
changed <- atomicModifyIORef' (immutableShares backend) $
\shares ->
case lookup storageIndex shares >>= lookup shareNumber of
Just _ ->
-- It is not allowed to write new data for an immutable share that
-- has already been written.
(shares, False)
Nothing ->
(addShares storageIndex [(shareNumber, shareData)] shares, True)
if changed
then return ()
else throwIO ImmutableShareAlreadyWritten
writeImmutableShare _ _ _ _ _ = error "writeImmutableShare got bad input"
adviseCorruptImmutableShare :: MemoryBackend -> StorageIndex -> ShareNumber -> CorruptionDetails -> IO ()
adviseCorruptImmutableShare _backend _ _ _ =
return mempty
getImmutableShareNumbers :: MemoryBackend -> StorageIndex -> IO (CBORSet ShareNumber)
getImmutableShareNumbers backend storageIndex = do
shares' <- readIORef $ immutableShares backend
return $ CBORSet . Set.fromList $ maybe [] keys $ lookup storageIndex shares'
readImmutableShare :: MemoryBackend -> StorageIndex -> ShareNumber -> QueryRange -> IO ShareData
readImmutableShare backend storageIndex shareNum _qr = do
shares' <- readIORef $ immutableShares backend
let result = case lookup storageIndex shares' of
Nothing -> mempty
Just shares'' -> lookup shareNum shares''
pure $ fromMaybe mempty result
totalShareSize :: MemoryBackend -> IO Size
totalShareSize backend = do
imm <- readIORef $ immutableShares backend
mut <- readIORef $ mutableShares backend
let immSize = sum $ map length imm
let mutSize = sum $ map length mut
return $ toInteger $ immSize + mutSize
addShares :: StorageIndex -> [(ShareNumber, ShareData)] -> ShareStorage -> ShareStorage
addShares _storageIndex [] shareStorage = shareStorage
addShares storageIndex ((shareNumber, shareData) : rest) shareStorage =
let added = case lookup storageIndex shareStorage of
Nothing ->
insert storageIndex (fromList [(shareNumber, shareData)]) shareStorage
Just _shares ->
adjust addShare' storageIndex shareStorage
where
addShare' = insert shareNumber shareData
in addShares storageIndex rest added
memoryBackend :: IO MemoryBackend
memoryBackend = do
immutableShares <- newIORef mempty
mutableShares <- newIORef mempty
buckets <- newIORef mempty
return $ MemoryBackend immutableShares mutableShares buckets