Establish host properties and isolated kind matrix

This commit is contained in:
ms2k 2026-07-18 00:54:47 +09:00
commit 06d1383456
2 changed files with 1076 additions and 120 deletions

View file

@ -14,17 +14,21 @@ import Data.Char (isControl)
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import qualified Data.Text.Normalize as TN
import Nuthatch.Types
validateRelativePath :: Text -> Either Text RelativePath
validateRelativePath raw = do
let path = T.dropWhile (== '/') raw
if T.null path
if T.null raw
then Left "path must not be empty"
else if T.head raw == '/' || T.last raw == '/'
then Left "path must not begin or end with a separator"
else if TN.normalize TN.NFC raw /= raw
then Left "path must be NFC-normalized"
else do
let segments = T.splitOn "/" path
let segments = T.splitOn "/" raw
mapM_ validateSegment segments
pure (RelativePath path)
pure (RelativePath raw)
where
validateSegment seg
| T.null seg = Left "empty path segment"

View file

@ -13,6 +13,11 @@ module Nuthatch.Protocol
, claimActiveBatch
, lookupMutation
, lookupObjectMeta
, sameFingerprint
, growthBytes
, quotaAllows
, terminalExpiryEligible
, recoveryEligible
, desiredMatches
, priorMatches
) where
@ -105,6 +110,21 @@ sameFingerprint rec kind key size msha =
&& mrDeclaredSize rec == size
&& mrContentSha256 rec == msha
growthBytes :: Integer -> Integer -> Integer
growthBytes priorSize desiredSize = max 0 (desiredSize - priorSize)
quotaAllows :: Integer -> Integer -> Integer -> Integer -> Bool
quotaAllows limitBytes usedBytes reservedBytes growth =
usedBytes + reservedBytes + growth <= limitBytes
terminalExpiryEligible :: MutationState -> ExternalState -> Bool
terminalExpiryEligible state external =
state `elem` [Succeeded, Failed, Rejected] && external == ExtIdle
recoveryEligible :: MutationState -> Integer -> Integer -> Bool
recoveryEligible state elapsedSeconds graceSeconds =
state `elem` [Prepared, Applying] && elapsedSeconds > graceSeconds
admitMutation
:: Connection
-> UserId
@ -131,7 +151,7 @@ admitMutation conn uid kind op pool key rel size msha maxObject = do
desiredSize = case kind of
Put -> size
Delete -> 0
growth = max 0 (desiredSize - priorSize)
growth = growthBytes priorSize desiredSize
baseRec state =
MutationRecord
{ mrUserId = uid
@ -416,13 +436,12 @@ desiredMatches rec = \case
Put -> False
ObjectPresent sz msha ->
case mrKind rec of
Delete -> sz == 0
Delete -> False
Put ->
sz == mrDeclaredSize rec
&& case (mrContentSha256 rec, msha) of
(Just want, Just got) -> want == got
-- Some stores omit custom metadata on HEAD.
(Just _, Nothing) -> True
(Just _, Nothing) -> False
(Nothing, _) -> True
priorMatches :: MutationRecord -> ObjectObservation -> Bool