riak 0.9.1.1 → 1.0.0.0
raw patch · 25 files changed
+1797/−120 lines, 25 filesdep +criteriondep +data-default-classdep +deepseqdep ~basedep ~bytestringdep ~protocol-buffersPVP ok
version bump matches the API change (PVP)
Dependencies added: criterion, data-default-class, deepseq, hashable, mtl, semigroups, unordered-containers
Dependency ranges changed: base, bytestring, protocol-buffers, riak-protobuf
API changes (from Hackage documentation)
+ Network.Riak.Basic: getBucketType :: Connection -> BucketType -> IO BucketProps
+ Network.Riak.CRDT: class MapCRDT a => CRDT a op | a -> op, op -> a
+ Network.Riak.CRDT: get :: Connection -> BucketType -> Bucket -> Key -> IO (Maybe DataType)
+ Network.Riak.CRDT: instance CRDT Counter CounterOp
+ Network.Riak.CRDT: instance CRDT Map MapOp
+ Network.Riak.CRDT: instance CRDT Set SetOp
+ Network.Riak.CRDT: instance MapCRDT Counter
+ Network.Riak.CRDT: instance MapCRDT Flag
+ Network.Riak.CRDT: instance MapCRDT Map
+ Network.Riak.CRDT: instance MapCRDT Register
+ Network.Riak.CRDT: instance MapCRDT Set
+ Network.Riak.CRDT: modify :: CRDT a op => op -> a -> a
+ Network.Riak.CRDT: sendModify :: CRDT a op => Connection -> BucketType -> Bucket -> Key -> [op] -> IO ()
+ Network.Riak.CRDT.Ops: SetOpsComb :: Set ByteString -> Set ByteString -> SetOpsComb
+ Network.Riak.CRDT.Ops: counterUpdateOp :: [CounterOp] -> DtOp
+ Network.Riak.CRDT.Ops: data SetOpsComb
+ Network.Riak.CRDT.Ops: instance Monoid SetOpsComb
+ Network.Riak.CRDT.Ops: instance Show SetOpsComb
+ Network.Riak.CRDT.Ops: mapUpdateOp :: [MapOp] -> DtOp
+ Network.Riak.CRDT.Ops: setAdds :: SetOpsComb -> Set ByteString
+ Network.Riak.CRDT.Ops: setRemoves :: SetOpsComb -> Set ByteString
+ Network.Riak.CRDT.Ops: setUpdateOp :: [SetOp] -> DtOp
+ Network.Riak.CRDT.Ops: toOpsComb :: SetOp -> SetOpsComb
+ Network.Riak.CRDT.Request: counterUpdate :: [CounterOp] -> BucketType -> Bucket -> Key -> DtUpdateRequest
+ Network.Riak.CRDT.Request: get :: ByteString -> ByteString -> ByteString -> DtFetchRequest
+ Network.Riak.CRDT.Request: mapUpdate :: [MapOp] -> BucketType -> Bucket -> Key -> DtUpdateRequest
+ Network.Riak.CRDT.Request: setUpdate :: [SetOp] -> BucketType -> Bucket -> Key -> DtUpdateRequest
+ Network.Riak.CRDT.Response: get :: DtFetchResponse -> Maybe DataType
+ Network.Riak.CRDT.Riak: counterSendUpdate :: Connection -> BucketType -> Bucket -> Key -> [CounterOp] -> IO ()
+ Network.Riak.CRDT.Riak: get :: Connection -> BucketType -> Bucket -> Key -> IO (Maybe DataType)
+ Network.Riak.CRDT.Riak: mapSendUpdate :: Connection -> BucketType -> Bucket -> Key -> [MapOp] -> IO ()
+ Network.Riak.CRDT.Riak: setSendUpdate :: Connection -> BucketType -> Bucket -> Key -> [SetOp] -> IO ()
+ Network.Riak.CRDT.Types: (-/) :: ByteString -> MapPath -> MapPath
+ Network.Riak.CRDT.Types: (:|) :: a -> [a] -> NonEmpty a
+ Network.Riak.CRDT.Types: Counter :: Count -> Counter
+ Network.Riak.CRDT.Types: CounterInc :: !Count -> CounterOp
+ Network.Riak.CRDT.Types: DTCounter :: Counter -> DataType
+ Network.Riak.CRDT.Types: DTMap :: Map -> DataType
+ Network.Riak.CRDT.Types: DTSet :: Set -> DataType
+ Network.Riak.CRDT.Types: Flag :: Bool -> Flag
+ Network.Riak.CRDT.Types: FlagSet :: !Bool -> FlagOp
+ Network.Riak.CRDT.Types: Map :: MapContent -> Map
+ Network.Riak.CRDT.Types: MapCounter :: !Counter -> MapEntry
+ Network.Riak.CRDT.Types: MapCounterOp :: !CounterOp -> MapValueOp
+ Network.Riak.CRDT.Types: MapCounterTag :: MapEntryTag
+ Network.Riak.CRDT.Types: MapField :: MapEntryTag -> ByteString -> MapField
+ Network.Riak.CRDT.Types: MapFlag :: !Flag -> MapEntry
+ Network.Riak.CRDT.Types: MapFlagOp :: !FlagOp -> MapValueOp
+ Network.Riak.CRDT.Types: MapFlagTag :: MapEntryTag
+ Network.Riak.CRDT.Types: MapMap :: !Map -> MapEntry
+ Network.Riak.CRDT.Types: MapMapOp :: !MapOp -> MapValueOp
+ Network.Riak.CRDT.Types: MapMapTag :: MapEntryTag
+ Network.Riak.CRDT.Types: MapPath :: (NonEmpty ByteString) -> MapPath
+ Network.Riak.CRDT.Types: MapRegister :: !Register -> MapEntry
+ Network.Riak.CRDT.Types: MapRegisterOp :: !RegisterOp -> MapValueOp
+ Network.Riak.CRDT.Types: MapRegisterTag :: MapEntryTag
+ Network.Riak.CRDT.Types: MapRemove :: MapField -> MapOp
+ Network.Riak.CRDT.Types: MapSet :: !Set -> MapEntry
+ Network.Riak.CRDT.Types: MapSetOp :: !SetOp -> MapValueOp
+ Network.Riak.CRDT.Types: MapSetTag :: MapEntryTag
+ Network.Riak.CRDT.Types: MapUpdate :: MapPath -> MapValueOp -> MapOp
+ Network.Riak.CRDT.Types: Register :: ByteString -> Register
+ Network.Riak.CRDT.Types: RegisterSet :: !ByteString -> RegisterOp
+ Network.Riak.CRDT.Types: Set :: (Set ByteString) -> Set
+ Network.Riak.CRDT.Types: SetAdd :: ByteString -> SetOp
+ Network.Riak.CRDT.Types: SetRemove :: ByteString -> SetOp
+ Network.Riak.CRDT.Types: data CounterOp
+ Network.Riak.CRDT.Types: data DataType
+ Network.Riak.CRDT.Types: data FlagOp
+ Network.Riak.CRDT.Types: data MapEntry
+ Network.Riak.CRDT.Types: data MapEntryTag
+ Network.Riak.CRDT.Types: data MapField
+ Network.Riak.CRDT.Types: data MapOp
+ Network.Riak.CRDT.Types: data MapValueOp
+ Network.Riak.CRDT.Types: data NonEmpty a :: * -> *
+ Network.Riak.CRDT.Types: data RegisterOp
+ Network.Riak.CRDT.Types: data SetOp
+ Network.Riak.CRDT.Types: instance Constructor C1_0Counter
+ Network.Riak.CRDT.Types: instance Constructor C1_0DataType
+ Network.Riak.CRDT.Types: instance Constructor C1_0Flag
+ Network.Riak.CRDT.Types: instance Constructor C1_0Map
+ Network.Riak.CRDT.Types: instance Constructor C1_0MapEntry
+ Network.Riak.CRDT.Types: instance Constructor C1_0MapEntryTag
+ Network.Riak.CRDT.Types: instance Constructor C1_0MapField
+ Network.Riak.CRDT.Types: instance Constructor C1_0Register
+ Network.Riak.CRDT.Types: instance Constructor C1_0Set
+ Network.Riak.CRDT.Types: instance Constructor C1_1DataType
+ Network.Riak.CRDT.Types: instance Constructor C1_1MapEntry
+ Network.Riak.CRDT.Types: instance Constructor C1_1MapEntryTag
+ Network.Riak.CRDT.Types: instance Constructor C1_2DataType
+ Network.Riak.CRDT.Types: instance Constructor C1_2MapEntry
+ Network.Riak.CRDT.Types: instance Constructor C1_2MapEntryTag
+ Network.Riak.CRDT.Types: instance Constructor C1_3MapEntry
+ Network.Riak.CRDT.Types: instance Constructor C1_3MapEntryTag
+ Network.Riak.CRDT.Types: instance Constructor C1_4MapEntry
+ Network.Riak.CRDT.Types: instance Constructor C1_4MapEntryTag
+ Network.Riak.CRDT.Types: instance Datatype D1Counter
+ Network.Riak.CRDT.Types: instance Datatype D1DataType
+ Network.Riak.CRDT.Types: instance Datatype D1Flag
+ Network.Riak.CRDT.Types: instance Datatype D1Map
+ Network.Riak.CRDT.Types: instance Datatype D1MapEntry
+ Network.Riak.CRDT.Types: instance Datatype D1MapEntryTag
+ Network.Riak.CRDT.Types: instance Datatype D1MapField
+ Network.Riak.CRDT.Types: instance Datatype D1Register
+ Network.Riak.CRDT.Types: instance Datatype D1Set
+ Network.Riak.CRDT.Types: instance Default Counter
+ Network.Riak.CRDT.Types: instance Default Flag
+ Network.Riak.CRDT.Types: instance Default Map
+ Network.Riak.CRDT.Types: instance Default Register
+ Network.Riak.CRDT.Types: instance Default Set
+ Network.Riak.CRDT.Types: instance Eq Counter
+ Network.Riak.CRDT.Types: instance Eq CounterOp
+ Network.Riak.CRDT.Types: instance Eq DataType
+ Network.Riak.CRDT.Types: instance Eq Flag
+ Network.Riak.CRDT.Types: instance Eq FlagOp
+ Network.Riak.CRDT.Types: instance Eq Map
+ Network.Riak.CRDT.Types: instance Eq MapEntry
+ Network.Riak.CRDT.Types: instance Eq MapEntryTag
+ Network.Riak.CRDT.Types: instance Eq MapField
+ Network.Riak.CRDT.Types: instance Eq MapOp
+ Network.Riak.CRDT.Types: instance Eq MapPath
+ Network.Riak.CRDT.Types: instance Eq MapValueOp
+ Network.Riak.CRDT.Types: instance Eq Register
+ Network.Riak.CRDT.Types: instance Eq RegisterOp
+ Network.Riak.CRDT.Types: instance Eq Set
+ Network.Riak.CRDT.Types: instance Eq SetOp
+ Network.Riak.CRDT.Types: instance Generic Counter
+ Network.Riak.CRDT.Types: instance Generic DataType
+ Network.Riak.CRDT.Types: instance Generic Flag
+ Network.Riak.CRDT.Types: instance Generic Map
+ Network.Riak.CRDT.Types: instance Generic MapEntry
+ Network.Riak.CRDT.Types: instance Generic MapEntryTag
+ Network.Riak.CRDT.Types: instance Generic MapField
+ Network.Riak.CRDT.Types: instance Generic Register
+ Network.Riak.CRDT.Types: instance Generic Set
+ Network.Riak.CRDT.Types: instance IsMapOp CounterOp
+ Network.Riak.CRDT.Types: instance IsMapOp FlagOp
+ Network.Riak.CRDT.Types: instance IsMapOp RegisterOp
+ Network.Riak.CRDT.Types: instance IsMapOp SetOp
+ Network.Riak.CRDT.Types: instance IsString MapPath
+ Network.Riak.CRDT.Types: instance Monoid Counter
+ Network.Riak.CRDT.Types: instance Monoid CounterOp
+ Network.Riak.CRDT.Types: instance Monoid Flag
+ Network.Riak.CRDT.Types: instance Monoid Register
+ Network.Riak.CRDT.Types: instance Monoid Set
+ Network.Riak.CRDT.Types: instance NFData Counter
+ Network.Riak.CRDT.Types: instance NFData DataType
+ Network.Riak.CRDT.Types: instance NFData Flag
+ Network.Riak.CRDT.Types: instance NFData Map
+ Network.Riak.CRDT.Types: instance NFData MapEntry
+ Network.Riak.CRDT.Types: instance NFData MapEntryTag
+ Network.Riak.CRDT.Types: instance NFData MapField
+ Network.Riak.CRDT.Types: instance NFData Register
+ Network.Riak.CRDT.Types: instance NFData Set
+ Network.Riak.CRDT.Types: instance Num Counter
+ Network.Riak.CRDT.Types: instance Ord Counter
+ Network.Riak.CRDT.Types: instance Ord Flag
+ Network.Riak.CRDT.Types: instance Ord MapEntryTag
+ Network.Riak.CRDT.Types: instance Ord MapField
+ Network.Riak.CRDT.Types: instance Ord Set
+ Network.Riak.CRDT.Types: instance Semigroup Counter
+ Network.Riak.CRDT.Types: instance Semigroup Flag
+ Network.Riak.CRDT.Types: instance Semigroup Register
+ Network.Riak.CRDT.Types: instance Semigroup Set
+ Network.Riak.CRDT.Types: instance Show Counter
+ Network.Riak.CRDT.Types: instance Show CounterOp
+ Network.Riak.CRDT.Types: instance Show DataType
+ Network.Riak.CRDT.Types: instance Show Flag
+ Network.Riak.CRDT.Types: instance Show FlagOp
+ Network.Riak.CRDT.Types: instance Show Map
+ Network.Riak.CRDT.Types: instance Show MapEntry
+ Network.Riak.CRDT.Types: instance Show MapEntryTag
+ Network.Riak.CRDT.Types: instance Show MapField
+ Network.Riak.CRDT.Types: instance Show MapOp
+ Network.Riak.CRDT.Types: instance Show MapPath
+ Network.Riak.CRDT.Types: instance Show MapValueOp
+ Network.Riak.CRDT.Types: instance Show Register
+ Network.Riak.CRDT.Types: instance Show RegisterOp
+ Network.Riak.CRDT.Types: instance Show Set
+ Network.Riak.CRDT.Types: instance Show SetOp
+ Network.Riak.CRDT.Types: instance Show op => Show (MapOp_ op)
+ Network.Riak.CRDT.Types: mapEntryTag :: MapValueOp -> MapEntryTag
+ Network.Riak.CRDT.Types: mapUpdate :: IsMapOp o => MapPath -> o -> MapOp
+ Network.Riak.CRDT.Types: newtype Counter
+ Network.Riak.CRDT.Types: newtype Flag
+ Network.Riak.CRDT.Types: newtype Map
+ Network.Riak.CRDT.Types: newtype MapPath
+ Network.Riak.CRDT.Types: newtype Register
+ Network.Riak.CRDT.Types: newtype Set
+ Network.Riak.CRDT.Types: setFromSeq :: Seq ByteString -> Set
+ Network.Riak.CRDT.Types: type Count = Int64
+ Network.Riak.CRDT.Types: type MapContent = Map MapField MapEntry
+ Network.Riak.CRDT.Types: xlookup :: MapPath -> MapEntryTag -> Map -> Maybe MapEntry
+ Network.Riak.Connection.Internal: connect :: Client -> IO Connection
+ Network.Riak.Connection.Internal: defaultClient :: Client
+ Network.Riak.Connection.Internal: disconnect :: Connection -> IO ()
+ Network.Riak.Connection.Internal: exchange :: Exchange req resp => Connection -> req -> IO resp
+ Network.Riak.Connection.Internal: exchangeMaybe :: Exchange req resp => Connection -> req -> IO (Maybe resp)
+ Network.Riak.Connection.Internal: exchange_ :: Request req => Connection -> req -> IO ()
+ Network.Riak.Connection.Internal: instance Exception ErrorResponse
+ Network.Riak.Connection.Internal: makeClientID :: IO ClientID
+ Network.Riak.Connection.Internal: pipeline :: Exchange req resp => Connection -> [req] -> IO [resp]
+ Network.Riak.Connection.Internal: pipelineMaybe :: Exchange req resp => Connection -> [req] -> IO [Maybe resp]
+ Network.Riak.Connection.Internal: pipeline_ :: Request req => Connection -> [req] -> IO ()
+ Network.Riak.Connection.Internal: recvMaybeResponse :: Response a => Connection -> IO (Maybe a)
+ Network.Riak.Connection.Internal: recvResponse :: Response a => Connection -> IO a
+ Network.Riak.Connection.Internal: recvResponse_ :: Connection -> MessageTag -> IO ()
+ Network.Riak.Connection.Internal: sendRequest :: Request req => Connection -> req -> IO ()
+ Network.Riak.Connection.Internal: setClientID :: Connection -> ClientID -> IO ()
+ Network.Riak.Request: getBucketType :: BucketType -> GetBucketTypeRequest
+ Network.Riak.Request: getIndex :: Maybe Index -> YzIndexGetRequest
+ Network.Riak.Request: search :: SearchQuery -> Index -> SearchQueryRequest
+ Network.Riak.Response: getIndex :: YzIndexGetResponse -> [IndexInfo]
+ Network.Riak.Response: search :: SearchQueryResponse -> [SearchResult]
+ Network.Riak.Search: getIndex :: Connection -> Maybe Index -> IO [IndexInfo]
+ Network.Riak.Search: searchRaw :: Connection -> SearchQuery -> Index -> IO [SearchResult]
+ Network.Riak.Types: DtFetchRequest :: MessageTag
+ Network.Riak.Types: DtFetchResponse :: MessageTag
+ Network.Riak.Types: DtUpdateRequest :: MessageTag
+ Network.Riak.Types: DtUpdateResponse :: MessageTag
+ Network.Riak.Types: GetBucketTypeRequest :: MessageTag
+ Network.Riak.Types: SearchQueryRequest :: MessageTag
+ Network.Riak.Types: SearchQueryResponse :: MessageTag
+ Network.Riak.Types: SearchResult :: BucketType -> Bucket -> Key -> Maybe Score -> SearchResult
+ Network.Riak.Types: YokozunaIndexGetRequest :: MessageTag
+ Network.Riak.Types: YokozunaIndexGetResponse :: MessageTag
+ Network.Riak.Types: bucket :: SearchResult -> Bucket
+ Network.Riak.Types: bucketType :: SearchResult -> BucketType
+ Network.Riak.Types: data SearchResult
+ Network.Riak.Types: key :: SearchResult -> Key
+ Network.Riak.Types: score :: SearchResult -> Maybe Score
+ Network.Riak.Types: type BucketType = ByteString
+ Network.Riak.Types.Internal: All :: Quorum
+ Network.Riak.Types.Internal: Client :: HostName -> ServiceName -> ClientID -> Client
+ Network.Riak.Types.Internal: Connection :: Socket -> Client -> IORef ByteString -> Connection
+ Network.Riak.Types.Internal: Default :: Quorum
+ Network.Riak.Types.Internal: DeleteRequest :: MessageTag
+ Network.Riak.Types.Internal: DeleteResponse :: MessageTag
+ Network.Riak.Types.Internal: DtFetchRequest :: MessageTag
+ Network.Riak.Types.Internal: DtFetchResponse :: MessageTag
+ Network.Riak.Types.Internal: DtUpdateRequest :: MessageTag
+ Network.Riak.Types.Internal: DtUpdateResponse :: MessageTag
+ Network.Riak.Types.Internal: Erlang :: ByteString -> Job
+ Network.Riak.Types.Internal: ErrorResponse :: MessageTag
+ Network.Riak.Types.Internal: GetBucketRequest :: MessageTag
+ Network.Riak.Types.Internal: GetBucketResponse :: MessageTag
+ Network.Riak.Types.Internal: GetBucketTypeRequest :: MessageTag
+ Network.Riak.Types.Internal: GetClientIDRequest :: MessageTag
+ Network.Riak.Types.Internal: GetClientIDResponse :: MessageTag
+ Network.Riak.Types.Internal: GetRequest :: MessageTag
+ Network.Riak.Types.Internal: GetResponse :: MessageTag
+ Network.Riak.Types.Internal: GetServerInfoRequest :: MessageTag
+ Network.Riak.Types.Internal: GetServerInfoResponse :: MessageTag
+ Network.Riak.Types.Internal: IndexBin :: !Index -> !ByteString -> IndexValue
+ Network.Riak.Types.Internal: IndexInt :: !Index -> !Int -> IndexValue
+ Network.Riak.Types.Internal: IndexQueryExactBin :: !Index -> !ByteString -> IndexQuery
+ Network.Riak.Types.Internal: IndexQueryExactInt :: !Index -> !Int -> IndexQuery
+ Network.Riak.Types.Internal: IndexQueryRangeBin :: !Index -> !ByteString -> !ByteString -> IndexQuery
+ Network.Riak.Types.Internal: IndexQueryRangeInt :: !Index -> !Int -> !Int -> IndexQuery
+ Network.Riak.Types.Internal: IndexRequest :: MessageTag
+ Network.Riak.Types.Internal: IndexResponse :: MessageTag
+ Network.Riak.Types.Internal: JSON :: ByteString -> Job
+ Network.Riak.Types.Internal: ListBucketsRequest :: MessageTag
+ Network.Riak.Types.Internal: ListBucketsResponse :: MessageTag
+ Network.Riak.Types.Internal: ListKeysRequest :: MessageTag
+ Network.Riak.Types.Internal: ListKeysResponse :: MessageTag
+ Network.Riak.Types.Internal: MapReduceRequest :: MessageTag
+ Network.Riak.Types.Internal: MapReduceResponse :: MessageTag
+ Network.Riak.Types.Internal: One :: Quorum
+ Network.Riak.Types.Internal: PingRequest :: MessageTag
+ Network.Riak.Types.Internal: PingResponse :: MessageTag
+ Network.Riak.Types.Internal: PutRequest :: MessageTag
+ Network.Riak.Types.Internal: PutResponse :: MessageTag
+ Network.Riak.Types.Internal: Quorum :: Quorum
+ Network.Riak.Types.Internal: SearchQueryRequest :: MessageTag
+ Network.Riak.Types.Internal: SearchQueryResponse :: MessageTag
+ Network.Riak.Types.Internal: SearchResult :: BucketType -> Bucket -> Key -> Maybe Score -> SearchResult
+ Network.Riak.Types.Internal: SetBucketRequest :: MessageTag
+ Network.Riak.Types.Internal: SetBucketResponse :: MessageTag
+ Network.Riak.Types.Internal: SetClientIDRequest :: MessageTag
+ Network.Riak.Types.Internal: SetClientIDResponse :: MessageTag
+ Network.Riak.Types.Internal: VClock :: ByteString -> VClock
+ Network.Riak.Types.Internal: YokozunaIndexGetRequest :: MessageTag
+ Network.Riak.Types.Internal: YokozunaIndexGetResponse :: MessageTag
+ Network.Riak.Types.Internal: bucket :: SearchResult -> Bucket
+ Network.Riak.Types.Internal: bucketType :: SearchResult -> BucketType
+ Network.Riak.Types.Internal: class (Request req, Response resp) => Exchange req resp | req -> resp
+ Network.Riak.Types.Internal: class (Tagged msg, ReflectDescriptor msg, Show msg, Wire msg) => Request msg
+ Network.Riak.Types.Internal: class (Tagged msg, ReflectDescriptor msg, Show msg, Wire msg) => Response msg
+ Network.Riak.Types.Internal: class Tagged msg
+ Network.Riak.Types.Internal: clientID :: Client -> ClientID
+ Network.Riak.Types.Internal: connBuffer :: Connection -> IORef ByteString
+ Network.Riak.Types.Internal: connClient :: Connection -> Client
+ Network.Riak.Types.Internal: connSock :: Connection -> Socket
+ Network.Riak.Types.Internal: data Client
+ Network.Riak.Types.Internal: data Connection
+ Network.Riak.Types.Internal: data IndexQuery
+ Network.Riak.Types.Internal: data IndexValue
+ Network.Riak.Types.Internal: data Job
+ Network.Riak.Types.Internal: data MessageTag
+ Network.Riak.Types.Internal: data Quorum
+ Network.Riak.Types.Internal: data RiakException
+ Network.Riak.Types.Internal: data SearchResult
+ Network.Riak.Types.Internal: expectedResponse :: Request msg => msg -> MessageTag
+ Network.Riak.Types.Internal: fromQuorum :: Quorum -> Maybe Word32
+ Network.Riak.Types.Internal: fromVClock :: VClock -> ByteString
+ Network.Riak.Types.Internal: host :: Client -> HostName
+ Network.Riak.Types.Internal: instance (Tagged a, Tagged b) => Tagged (Either a b)
+ Network.Riak.Types.Internal: instance Bounded Quorum
+ Network.Riak.Types.Internal: instance Constructor C1_0MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_10MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_11MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_12MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_13MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_14MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_15MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_16MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_17MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_18MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_19MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_1MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_20MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_21MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_22MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_23MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_24MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_25MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_26MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_27MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_28MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_29MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_2MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_30MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_31MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_32MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_33MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_34MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_35MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_3MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_4MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_5MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_6MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_7MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_8MessageTag
+ Network.Riak.Types.Internal: instance Constructor C1_9MessageTag
+ Network.Riak.Types.Internal: instance Datatype D1MessageTag
+ Network.Riak.Types.Internal: instance Enum Quorum
+ Network.Riak.Types.Internal: instance Eq Client
+ Network.Riak.Types.Internal: instance Eq Connection
+ Network.Riak.Types.Internal: instance Eq IndexQuery
+ Network.Riak.Types.Internal: instance Eq IndexValue
+ Network.Riak.Types.Internal: instance Eq Job
+ Network.Riak.Types.Internal: instance Eq MessageTag
+ Network.Riak.Types.Internal: instance Eq Quorum
+ Network.Riak.Types.Internal: instance Eq RiakException
+ Network.Riak.Types.Internal: instance Eq SearchResult
+ Network.Riak.Types.Internal: instance Eq VClock
+ Network.Riak.Types.Internal: instance Exception RiakException
+ Network.Riak.Types.Internal: instance Generic MessageTag
+ Network.Riak.Types.Internal: instance Hashable MessageTag
+ Network.Riak.Types.Internal: instance Ord Quorum
+ Network.Riak.Types.Internal: instance Show Client
+ Network.Riak.Types.Internal: instance Show Connection
+ Network.Riak.Types.Internal: instance Show IndexQuery
+ Network.Riak.Types.Internal: instance Show IndexValue
+ Network.Riak.Types.Internal: instance Show Job
+ Network.Riak.Types.Internal: instance Show MessageTag
+ Network.Riak.Types.Internal: instance Show Quorum
+ Network.Riak.Types.Internal: instance Show RiakException
+ Network.Riak.Types.Internal: instance Show SearchResult
+ Network.Riak.Types.Internal: instance Show VClock
+ Network.Riak.Types.Internal: instance Tagged MessageTag
+ Network.Riak.Types.Internal: instance Typeable Client
+ Network.Riak.Types.Internal: instance Typeable Job
+ Network.Riak.Types.Internal: instance Typeable Quorum
+ Network.Riak.Types.Internal: instance Typeable RiakException
+ Network.Riak.Types.Internal: instance Typeable VClock
+ Network.Riak.Types.Internal: key :: SearchResult -> Key
+ Network.Riak.Types.Internal: messageTag :: Tagged msg => msg -> MessageTag
+ Network.Riak.Types.Internal: netError :: String -> String -> String -> a
+ Network.Riak.Types.Internal: newtype VClock
+ Network.Riak.Types.Internal: port :: Client -> ServiceName
+ Network.Riak.Types.Internal: score :: SearchResult -> Maybe Score
+ Network.Riak.Types.Internal: toQuorum :: Word32 -> Maybe Quorum
+ Network.Riak.Types.Internal: type Bucket = ByteString
+ Network.Riak.Types.Internal: type BucketType = ByteString
+ Network.Riak.Types.Internal: type ClientID = ByteString
+ Network.Riak.Types.Internal: type DW = Quorum
+ Network.Riak.Types.Internal: type Index = ByteString
+ Network.Riak.Types.Internal: type IndexInfo = YzIndex
+ Network.Riak.Types.Internal: type Key = ByteString
+ Network.Riak.Types.Internal: type R = Quorum
+ Network.Riak.Types.Internal: type RW = Quorum
+ Network.Riak.Types.Internal: type SearchQuery = ByteString
+ Network.Riak.Types.Internal: type Tag = ByteString
+ Network.Riak.Types.Internal: type W = Quorum
+ Network.Riak.Types.Internal: typeError :: String -> String -> String -> a
+ Network.Riak.Types.Internal: unexError :: String -> String -> String -> a
- Network.Riak: foldKeys :: MonadIO m => Connection -> Bucket -> (a -> Key -> m a) -> a -> m a
+ Network.Riak: foldKeys :: MonadIO m => Connection -> Maybe BucketType -> Bucket -> (a -> Key -> m a) -> a -> m a
- Network.Riak: getBucket :: Connection -> Bucket -> IO BucketProps
+ Network.Riak: getBucket :: Connection -> Maybe BucketType -> Bucket -> IO BucketProps
- Network.Riak: listBuckets :: Connection -> IO (Seq Bucket)
+ Network.Riak: listBuckets :: Connection -> Maybe BucketType -> IO (Seq Bucket)
- Network.Riak: setBucket :: Connection -> Bucket -> BucketProps -> IO ()
+ Network.Riak: setBucket :: Connection -> Maybe BucketType -> Bucket -> BucketProps -> IO ()
- Network.Riak.Basic: foldKeys :: MonadIO m => Connection -> Bucket -> (a -> Key -> m a) -> a -> m a
+ Network.Riak.Basic: foldKeys :: MonadIO m => Connection -> Maybe BucketType -> Bucket -> (a -> Key -> m a) -> a -> m a
- Network.Riak.Basic: getBucket :: Connection -> Bucket -> IO BucketProps
+ Network.Riak.Basic: getBucket :: Connection -> Maybe BucketType -> Bucket -> IO BucketProps
- Network.Riak.Basic: listBuckets :: Connection -> IO (Seq Bucket)
+ Network.Riak.Basic: listBuckets :: Connection -> Maybe BucketType -> IO (Seq Bucket)
- Network.Riak.Basic: setBucket :: Connection -> Bucket -> BucketProps -> IO ()
+ Network.Riak.Basic: setBucket :: Connection -> Maybe BucketType -> Bucket -> BucketProps -> IO ()
- Network.Riak.Request: getBucket :: Bucket -> GetBucketRequest
+ Network.Riak.Request: getBucket :: Maybe BucketType -> Bucket -> GetBucketRequest
- Network.Riak.Request: listBuckets :: ListBucketsRequest
+ Network.Riak.Request: listBuckets :: Maybe BucketType -> ListBucketsRequest
- Network.Riak.Request: listKeys :: Bucket -> ListKeysRequest
+ Network.Riak.Request: listKeys :: Maybe BucketType -> Bucket -> ListKeysRequest
- Network.Riak.Request: setBucket :: Bucket -> BucketProps -> SetBucketRequest
+ Network.Riak.Request: setBucket :: Maybe BucketType -> Bucket -> BucketProps -> SetBucketRequest
- Network.Riak.Types: class (Request req, Response resp) => Exchange req resp | req -> resp, resp -> req
+ Network.Riak.Types: class (Request req, Response resp) => Exchange req resp | req -> resp
Files
- Changes.md +8/−0
- benchmarks/Main.hs +133/−0
- riak.cabal +47/−11
- src/Network/Riak.hs +2/−0
- src/Network/Riak/Basic.hs +15/−9
- src/Network/Riak/CRDT.hs +172/−0
- src/Network/Riak/CRDT/Ops.hs +118/−0
- src/Network/Riak/CRDT/Request.hs +63/−0
- src/Network/Riak/CRDT/Response.hs +56/−0
- src/Network/Riak/CRDT/Riak.hs +35/−0
- src/Network/Riak/CRDT/Types.hs +284/−0
- src/Network/Riak/Connection/Internal.hs +0/−2
- src/Network/Riak/JSON.hs +11/−11
- src/Network/Riak/JSON/Resolvable.hs +2/−2
- src/Network/Riak/Request.hs +58/−15
- src/Network/Riak/Resolvable/Internal.hs +16/−16
- src/Network/Riak/Response.hs +42/−5
- src/Network/Riak/Search.hs +28/−0
- src/Network/Riak/Tag.hs +161/−4
- src/Network/Riak/Types.hs +2/−0
- src/Network/Riak/Types/Internal.hs +52/−12
- tests/CRDTProperties.hs +309/−0
- tests/Common.hs +17/−0
- tests/Properties.hs +36/−19
- tests/Test.hs +130/−14
+ Changes.md view
@@ -0,0 +1,8 @@+* 1.0+ - No longer expected to work with riak <= 2+ - CRDT functionality, including high-level interface+ and quickCheck tests+ - Basic yokozuna search functionality+ - CRDT benchmarking suite+ - Bugfixes+ - Tested on both 2.0.x and 2.1.x
+ benchmarks/Main.hs view
@@ -0,0 +1,133 @@+{-# LANGUAGE OverloadedStrings #-}+module Main where++import Criterion.Main++import Network.Riak.Connection.Pool as Pool (Pool, create, withConnection)+import Network.Riak.Connection (defaultClient)+import Network.Riak.CRDT+import qualified Data.ByteString.Lazy.Char8 as B+import Control.Applicative+import Control.Monad+import Data.Maybe+import Data.String+import qualified Data.List.NonEmpty as NEL+import Data.Semigroup+++main :: IO ()+main = benchmarks >>= defaultMain++bucket = "bench"++benchmarks = do+ pool <- Pool.create defaultClient 1 1 1+ already <- Pool.withConnection pool $ \c -> get c "counters" bucket "__setup"+ when (isNothing already) $ setup pool+ pure [crdt pool]++setup :: Pool -> IO ()+setup pool = Pool.withConnection pool $ \c -> do+ setupSetN c "10" 10+ setupSetN c "100" 100+ setupSetN c "1000" 1000++ setupMapN_elem c "1" 1+ setupMapN_elem c "10-elem" 10+ setupMapN_elem c "100-elem" 100+ setupMapN_elem c "1000-elem" 1000+ setupMapN_nest c "10-depth" 10+ setupMapN_nest c "100-depth" 100+ setupMapN_nest c "1000-depth" 1000++ sendModify c "counters" bucket "__setup" [CounterInc 1]+ putStrLn "Bench env set up."+++-- | * Common things++-- | Key names+setK n = fromString $ show n+mapElemK n = fromString $ show n <> "-elem"+mapDepthK n = fromString $ show n <> "-depth"++-- | Map things+nestedMapPath n = MapPath . NEL.fromList $ [ fromString (show i) | i <- [1..n] ]+++setupSetN c key n = sequence_ [+ sendModify c "sets" bucket key [SetAdd (B.pack $ show i)]+ | i <- [1..n]+ ]++-- | n-elem map with counters+setupMapN_elem c key n = sendModify c "maps" bucket key ops+ where ops = [MapUpdate (MapPath $ (fromString $ show i) :| [])+ (MapCounterOp $ CounterInc 1)+ | i <- [1..n]+ ]++-- | n-depth map with a counter+setupMapN_nest c key n = sendModify c "maps" bucket key [op]+ where op = MapUpdate (nestedMapPath n)+ (MapCounterOp $ CounterInc 1)++++crdt :: Pool -> Benchmark+crdt pool = bgroup "CRDT" [+ bgroup "get" [+ bench "get a non-existent counter" . nfIO $ pooled getEmpty+ ],++ bgroup "counters" [+ bench "get a counter" . nfIO $ pooled getCounter+ ],++ bgroup "sets" [+ bench "get a 10-elem set" . nfIO $ pooled getSet10,+ bench "get a 100-elem set" . nfIO $ pooled getSet100,+ bench "get a 1000-elem set" . nfIO $ pooled getSet1000,+ bench "1000-set add+remove an elem cycle" . nfIO $ pooled set1kAddRemove+ ],++ bgroup "maps" [+ bgroup "get" [+ bench "get a trivial map" . nfIO . pooled $ get1Map,+ bench "get a 10-elem map" . nfIO . pooled $ getNMap 10,+ bench "get a 100-elem map" . nfIO . pooled $ getNMap 100,+ bench "get a 1000-elem map" . nfIO . pooled $ getNMap 1000,+ bench "get a 10-depth map" . nfIO . pooled $ getDMap 10,+ bench "get a 100-depth map" . nfIO . pooled $ getDMap 100,+ bench "get a 1000-depth map" . nfIO . pooled $ getDMap 1000+ ],++ bgroup "update" [+ bench "update a counter inside 10-elem map" . nfIO . pooled $ updateNMap 10,+ bench "update a counter inside 100-elem map" . nfIO . pooled $ updateNMap 100,+ bench "update a counter inside 1000-elem map" . nfIO . pooled $ updateNMap 1000,+ bench "update a counter inside 10-depth map" . nfIO . pooled $ updateDMap 10,+ bench "update a counter inside 100-depth map" . nfIO . pooled $ updateDMap 100,+ bench "update a counter inside 1000-depth map" . nfIO . pooled $ updateDMap 1000+ ]+ ]+ ]+ where pooled = Pool.withConnection pool+++getEmpty c = get c "counters" "not here" "never was"+getCounter c = get c "counters" "xxx" "yyy"+getSet10 c = get c "sets" bucket "10"+getSet100 c = get c "sets" bucket "100"+getSet1000 c = get c "sets" bucket "1000"++set1kAddRemove c = sequence_ [ sendModify c "sets" bucket "1000" [o]+ | o <- [SetAdd "foo", SetRemove "foo"] ]++get1Map c = get c "maps" bucket "1"+getNMap n c = get c "maps" bucket (mapElemK n)+getDMap n c = get c "maps" bucket (mapDepthK n)++updateNMap n c = setupMapN_elem c (mapElemK n) n+updateDMap n c = setupMapN_nest c (mapDepthK n) n+
riak.cabal view
@@ -1,5 +1,5 @@ name: riak-version: 0.9.1.1+version: 1.0.0.0 synopsis: A Haskell client for the Riak decentralized data store description: A Haskell client library for the Riak decentralized data@@ -26,8 +26,11 @@ [Network.Riak.Basic] manual storage, manual conflict resolution. This is the most demanding module to work with, as you must encode and decode data yourself, and handle all conflict resolution- yourself.+ yourself+ .+ [Network.Riak.CRDT] CRDT operations. + homepage: http://github.com/markhibberd/riak-haskell-client license: OtherLicense license-file: LICENSE@@ -38,7 +41,8 @@ category: Network build-type: Simple extra-source-files:- README.markdown+ README.markdown + Changes.md cabal-version: >=1.8 @@ -61,14 +65,20 @@ library hs-source-dirs: src- exposed-modules: Network.Riak Network.Riak.Basic Network.Riak.Cluster Network.Riak.Connection+ Network.Riak.Connection.Internal Network.Riak.Connection.Pool Network.Riak.Content+ Network.Riak.CRDT+ Network.Riak.CRDT.Ops+ Network.Riak.CRDT.Request+ Network.Riak.CRDT.Response+ Network.Riak.CRDT.Riak+ Network.Riak.CRDT.Types Network.Riak.Debug Network.Riak.Escape Network.Riak.Functions@@ -77,16 +87,16 @@ Network.Riak.Request Network.Riak.Resolvable Network.Riak.Response+ Network.Riak.Search Network.Riak.Types Network.Riak.Value Network.Riak.Value.Resolvable+ Network.Riak.Types.Internal other-modules:- Network.Riak.Connection.Internal Network.Riak.Connection.NoPush Network.Riak.Resolvable.Internal Network.Riak.Tag- Network.Riak.Types.Internal build-depends: aeson >= 0.8 && < 0.11,@@ -96,27 +106,33 @@ blaze-builder >= 0.3 && <= 0.5, bytestring, containers,+ data-default-class >= 0.0.1,+ deepseq >= 1.3, enclosed-exceptions >= 1.0.1.1 && <= 1.1, exceptions >= 0.8.0.2 && <= 0.9,+ hashable >= 1.2.3, transformers >= 0.3 && < 0.5, mersenne-random-pure64 >= 0.2.0.4 && < 0.3, monad-control >= 1.0.0.4 && < 1.1, network >= 2.3, resource-pool == 0.2.*,- protocol-buffers >= 2.1.4 && < 2.2,+ protocol-buffers >= 2.1.4 && < 2.3, pureMD5, random, random-shuffle >= 0.0.4 && < 0.1,- riak-protobuf == 0.20.*,+ riak-protobuf == 0.21.*,+ semigroups >= 0.16, text == 1.2.*, time >= 1.4.2 && < 1.6,- vector >= 0.10.12.3 && < 0.12+ vector >= 0.10.12.3 && < 0.12,+ unordered-containers >= 0.2.5 + if flag(debug) cpp-options: -DASSERTS -DDEBUG if flag(developer)- ghc-options: -Werror+ ghc-options: -Wall ghc-prof-options: -auto-all cpp-options: -DASSERTS -DDEBUG @@ -133,6 +149,8 @@ other-modules: Properties+ CRDTProperties+ Common build-depends: base,@@ -144,4 +162,22 @@ tasty, tasty-hunit, tasty-quickcheck,- text+ text,+ mtl >= 2.1,+ semigroups >= 0.16,+ data-default-class >= 0.0.1+++benchmark bench+ type: exitcode-stdio-1.0+ hs-source-dirs: benchmarks+ main-is: Main.hs+ build-depends:+ base,+ riak,+ criterion >= 1.1,+ bytestring >= 0.10,+ semigroups >= 0.16+++
src/Network/Riak.hs view
@@ -35,6 +35,8 @@ -- and decode data yourself, and handle all conflict resolution -- yourself. --+-- [Network.Riak.CRDT] CRDT operations.+-- -- A short getting started guide is available at <http://docs.basho.com/riak/latest/dev/taste-of-riak/haskell/> -- module Network.Riak
src/Network/Riak/Basic.hs view
@@ -39,6 +39,7 @@ , foldKeys , getBucket , setBucket+ , getBucketType -- * Map/reduce , mapReduce ) where@@ -114,15 +115,16 @@ -- List the buckets in the cluster. -- -- /Note/: this operation is expensive. Do not use it in production.-listBuckets :: Connection -> IO (Seq.Seq T.Bucket)-listBuckets conn = Resp.listBuckets <$> exchange conn Req.listBuckets+listBuckets :: Connection -> Maybe BucketType -> IO (Seq.Seq T.Bucket)+listBuckets conn btype = Resp.listBuckets <$> exchange conn (Req.listBuckets btype) -- Fold over the keys in a bucket. -- -- /Note/: this operation is expensive. Do not use it in production.-foldKeys :: (MonadIO m) => Connection -> T.Bucket -> (a -> Key -> m a) -> a -> m a-foldKeys conn bucket f z0 = do- liftIO $ sendRequest conn $ Req.listKeys bucket+foldKeys :: (MonadIO m) => Connection -> Maybe BucketType -> Bucket+ -> (a -> Key -> m a) -> a -> m a+foldKeys conn btype bucket f z0 = do+ liftIO $ sendRequest conn $ Req.listKeys btype bucket let g z = f z . unescape loop z = do ListKeysResponse{..} <- liftIO $ recvResponse conn@@ -133,12 +135,16 @@ loop z0 -- | Retrieve the properties of a bucket.-getBucket :: Connection -> T.Bucket -> IO BucketProps-getBucket conn bucket = Resp.getBucket <$> exchange conn (Req.getBucket bucket)+getBucket :: Connection -> Maybe BucketType -> Bucket -> IO BucketProps+getBucket conn btype bucket = Resp.getBucket <$> exchange conn (Req.getBucket btype bucket) -- | Store new properties for a bucket.-setBucket :: Connection -> T.Bucket -> BucketProps -> IO ()-setBucket conn bucket props = exchange_ conn $ Req.setBucket bucket props+setBucket :: Connection -> Maybe BucketType -> Bucket -> BucketProps -> IO ()+setBucket conn btype bucket props = exchange_ conn $ Req.setBucket btype bucket props++-- | Gets the bucket properties associated with a bucket type.+getBucketType :: Connection -> T.BucketType -> IO BucketProps+getBucketType conn btype = Resp.getBucket <$> exchange conn (Req.getBucketType btype) -- | Run a 'MapReduce' job. Its result is consumed via a strict left -- fold.
+ src/Network/Riak/CRDT.hs view
@@ -0,0 +1,172 @@+{- | module: Network.Riak.CRDT+ copyright: (c) 2016 Sentenai+ author: Antonio Nikishaev <me@lelf.lu>+ license: Apache++CRDT operations++* Haskell-side++ * Haskell values: 'Counter', 'Set' etc++ * ADT for operations: 'CounterOp', 'SetOp' etc++ * 'modify' to locally modify a value (matching riak behaviour)++* Riak-side++ * 'get' to get a current value++ * 'sendModify' to ask Riak to apply modifications++TL;DR example++>>> let c = Counter 41+>>> let op = CounterInc 1+>>> modify op c+Counter 42+>>> get conn "counters" "bucket" "key"+Just (DTCounter (Counter 41))+>>> sendModify conn "counters" "bucket" "key" [op] >> get conn "counters" "bucket" "key"+Just (DTCounter (Counter 42))++-}+{-# LANGUAGE TypeFamilies, OverloadedStrings, ScopedTypeVariables, PatternGuards #-}+{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}++module Network.Riak.CRDT (module Network.Riak.CRDT.Types,+ get,+ CRDT(..))+ where+++import Data.Default.Class+import qualified Data.Map as M+import Data.Proxy+import Data.Semigroup+import qualified Data.Set as S+import Network.Riak.CRDT.Ops+import Network.Riak.CRDT.Riak+import Network.Riak.CRDT.Types+import Network.Riak.Types+++-- | Modify a counter by applying operations ops+modifyCounter :: CounterOp -> Counter -> Counter+modifyCounter op c = c <> Counter i+ where CounterInc i = op++++-- | Modify a set by applying operations ops+modifySet :: SetOp -> Set -> Set+modifySet op (Set c) = Set (c `S.union` adds S.\\ rems)+ where SetOpsComb adds rems = toOpsComb op+++modifyMap :: MapOp -> Map -> Map+modifyMap (MapRemove field) (Map mc) = Map $ M.delete field mc+modifyMap (MapUpdate path op) m = modifyMap1 path op m+++modifyMap1 :: MapPath -> MapValueOp -> Map -> Map+modifyMap1 (MapPath (e :| [])) op m = modMap mf op m+ where mf = MapField (mapEntryTag op) e+modifyMap1 (MapPath (e :| (r:rs))) op (Map m')+ = Map $ M.alter (Just . f) (MapField MapMapTag e) m'+ where f :: Maybe MapEntry -> MapEntry+ f Nothing = f (Just $ MapMap def)+ f (Just (MapMap m)) = MapMap . modifyMap1 (MapPath (r :| rs)) op $ m+ f (Just z) = z++modMap :: MapField -> MapValueOp -> Map -> Map+modMap ix op (Map m) = Map $ M.alter (Just . modifyMapValue op) ix m+++modifyMapValue :: MapValueOp -> Maybe MapEntry -> MapEntry+modifyMapValue (MapSetOp op) = modifyEntry (Proxy :: Proxy Set) op+modifyMapValue (MapCounterOp op) = modifyEntry (Proxy :: Proxy Counter) op+modifyMapValue (MapMapOp op) = modifyEntry (Proxy :: Proxy Map) op+modifyMapValue (MapFlagOp op) = modifyEntry (Proxy :: Proxy Flag) op+modifyMapValue (MapRegisterOp op) = modifyEntry (Proxy :: Proxy Register) op+++modifyFlag :: FlagOp -> Flag -> Flag+modifyFlag (FlagSet x) = const (Flag x)++modifyRegister :: RegisterOp -> Register -> Register+modifyRegister (RegisterSet x) = const (Register x)+++-- | Types that can be held inside 'Map'+class Default a => MapCRDT a where+ type MapOperation_ a :: *+ mapModify :: MapOperation_ a -> a -> a++ -- | modify a maybe-absent 'MapEntry'+ modifyEntry :: Proxy a -> MapOperation_ a -> Maybe MapEntry -> MapEntry+ modifyEntry _ op Nothing = toEntry . mapModify op $ (def :: a)+ modifyEntry _ op (Just e) | Just v <- fromEntry e = toEntry . mapModify op $ (v :: a)+ | otherwise = e+ toEntry :: a -> MapEntry+ fromEntry :: MapEntry -> Maybe a+++instance MapCRDT Flag where+ type MapOperation_ Flag = FlagOp+ mapModify = modifyFlag+ fromEntry (MapFlag f) = Just f+ fromEntry _ = Nothing+ toEntry = MapFlag++instance MapCRDT Set where+ type MapOperation_ Set = SetOp+ mapModify = modify+ fromEntry (MapSet s) = Just s+ fromEntry _ = Nothing+ toEntry = MapSet++instance MapCRDT Counter where+ type MapOperation_ Counter = CounterOp+ mapModify = modify+ fromEntry (MapCounter s) = Just s+ fromEntry _ = Nothing+ toEntry = MapCounter++instance MapCRDT Register where+ type MapOperation_ Register = RegisterOp+ mapModify = modifyRegister+ fromEntry (MapRegister s) = Just s+ fromEntry _ = Nothing+ toEntry = MapRegister+++instance MapCRDT Map where+ type MapOperation_ Map = MapOp+ mapModify = modify+ fromEntry (MapMap s) = Just s+ fromEntry _ = Nothing+ toEntry = MapMap+++-- | CRDT types+class MapCRDT a => CRDT a op | a -> op, op -> a where+ -- | Modify a value by applying an operation+ modify :: op -> a -> a++ -- | Request riak a modification+ sendModify :: Connection+ -> BucketType -> Bucket -> Key+ -> [op] -> IO ()++instance CRDT Counter CounterOp where+ modify = modifyCounter+ sendModify = counterSendUpdate++instance CRDT Set SetOp where+ modify = modifySet+ sendModify = setSendUpdate++instance CRDT Map MapOp where+ modify = modifyMap+ sendModify = mapSendUpdate
+ src/Network/Riak/CRDT/Ops.hs view
@@ -0,0 +1,118 @@+-- | module: Network.Riak.CRDT.Ops+-- copyright: (c) 2016 Sentenai+-- author: Antonio Nikishaev <me@lelf.lu>+-- license: Apache+--+-- Conversions of CRDT operations to 'PB.DtOp'+--+module Network.Riak.CRDT.Ops (counterUpdateOp,+ setUpdateOp, SetOpsComb(..), toOpsComb,+ mapUpdateOp)+ where++import qualified Network.Riak.Protocol.DtOp as PB+import qualified Network.Riak.Protocol.CounterOp as PB+import qualified Network.Riak.Protocol.SetOp as PBSet++import qualified Network.Riak.Protocol.MapOp as PBMap+import qualified Network.Riak.Protocol.MapField as PBMap+import qualified Network.Riak.Protocol.MapField.MapFieldType as PBMap+import qualified Network.Riak.Protocol.MapUpdate as PBMap++import qualified Network.Riak.Protocol.MapUpdate.FlagOp as PBFlag++import Data.ByteString.Lazy (ByteString)+import Data.Monoid+import qualified Data.Sequence as Seq+import qualified Data.Set as S+import Network.Riak.CRDT.Types+++counterUpdateOp :: [CounterOp] -> PB.DtOp+counterUpdateOp ops = PB.DtOp { PB.counter_op = Just $ counterOpPB ops,+ PB.set_op = Nothing,+ PB.map_op = Nothing+ }++counterOpPB :: [CounterOp] -> PB.CounterOp+counterOpPB ops = PB.CounterOp (Just i)+ where CounterInc i = mconcat ops+++data SetOpsComb = SetOpsComb { setAdds :: S.Set ByteString,+ setRemoves :: S.Set ByteString }+ deriving (Show)++instance Monoid SetOpsComb where+ mempty = SetOpsComb mempty mempty+ (SetOpsComb a b) `mappend` (SetOpsComb x y) = SetOpsComb (a<>x) (b<>y)++toOpsComb :: SetOp -> SetOpsComb+toOpsComb (SetAdd s) = SetOpsComb (S.singleton s) S.empty+toOpsComb (SetRemove s) = SetOpsComb S.empty (S.singleton s)++++setUpdateOp :: [SetOp] -> PB.DtOp+setUpdateOp ops = PB.DtOp { PB.counter_op = Nothing,+ PB.set_op = Just $ setOpPB ops,+ PB.map_op = Nothing+ }++setOpPB :: [SetOp] -> PBSet.SetOp+setOpPB ops = PBSet.SetOp (toSeq adds) (toSeq rems)+ where SetOpsComb adds rems = mconcat . map toOpsComb $ ops+ toSeq = Seq.fromList . S.toList++flagOpPB :: FlagOp -> PBFlag.FlagOp+flagOpPB (FlagSet True) = PBFlag.ENABLE+flagOpPB (FlagSet False) = PBFlag.DISABLE++registerOpPB :: RegisterOp -> ByteString+registerOpPB (RegisterSet x) = x++mapUpdateOp :: [MapOp] -> PB.DtOp+mapUpdateOp ops = PB.DtOp { PB.counter_op = Nothing,+ PB.set_op = Nothing,+ PB.map_op = Just $ mapOpPB ops }++mapOpPB :: [MapOp] -> PBMap.MapOp+mapOpPB ops = PBMap.MapOp rems updates+ where rems = Seq.fromList [ toRemove f | MapRemove f <- ops ]+ updates = Seq.fromList [ toUpdate f u | MapUpdate f u <- ops ]++toRemove :: MapField -> PBMap.MapField+toRemove (MapField t name) = toField name t++toUpdate :: MapPath -> MapValueOp -> PBMap.MapUpdate+toUpdate (MapPath (e :| [])) op = toUpdate' e (mapEntryTag op) op+toUpdate (MapPath (e :| (r:rs))) op = toUpdate' e MapMapTag op'+ where op' = MapMapOp (MapUpdate (MapPath (r:|rs)) op)++toUpdate' :: ByteString -> MapEntryTag -> MapValueOp -> PBMap.MapUpdate+toUpdate' f t op = setSpecificOp op (updateNothing f t)++setSpecificOp :: MapValueOp -> PBMap.MapUpdate -> PBMap.MapUpdate+setSpecificOp (MapCounterOp cop) u = u { PBMap.counter_op = Just $ counterOpPB [cop] }+setSpecificOp (MapSetOp sop) u = u { PBMap.set_op = Just $ setOpPB [sop] }+setSpecificOp (MapRegisterOp rop) u = u { PBMap.register_op = Just $ registerOpPB rop }+setSpecificOp (MapFlagOp fop) u = u { PBMap.flag_op = Just $ flagOpPB fop }+setSpecificOp (MapMapOp mop) u = u { PBMap.map_op = Just $ mapOpPB [mop] }+++updateNothing :: ByteString -> MapEntryTag -> PBMap.MapUpdate+updateNothing f t = PBMap.MapUpdate { PBMap.field = toField f t,+ PBMap.counter_op = Nothing,+ PBMap.set_op = Nothing,+ PBMap.register_op = Nothing,+ PBMap.flag_op = Nothing,+ PBMap.map_op = Nothing }++toField :: ByteString -> MapEntryTag -> PBMap.MapField+toField name t = PBMap.MapField { PBMap.name = name,+ PBMap.type' = typ t }+ where typ MapCounterTag = PBMap.COUNTER+ typ MapSetTag = PBMap.SET+ typ MapRegisterTag = PBMap.REGISTER+ typ MapFlagTag = PBMap.FLAG+ typ MapMapTag = PBMap.MAP
+ src/Network/Riak/CRDT/Request.hs view
@@ -0,0 +1,63 @@+-- | module: Network.Riak.CRDT.Request+-- copyright: (c) 2016 Sentenai+-- author: Antonio Nikishaev <me@lelf.lu>+-- license: Apache+--+module Network.Riak.CRDT.Request+ (get, counterUpdate, setUpdate, mapUpdate) where++import Data.ByteString.Lazy (ByteString)+import Network.Riak.CRDT.Ops+import qualified Network.Riak.CRDT.Types as CRDT+import qualified Network.Riak.Protocol.DtFetchRequest as DtFetch+import qualified Network.Riak.Protocol.DtOp as DtOp+import qualified Network.Riak.Protocol.DtUpdateRequest as DtUpdate+import Network.Riak.Types++counterUpdate :: [CRDT.CounterOp]+ -> BucketType -> Bucket -> Key+ -> DtUpdate.DtUpdateRequest+counterUpdate ops = update (counterUpdateOp ops)++setUpdate :: [CRDT.SetOp]+ -> BucketType -> Bucket -> Key+ -> DtUpdate.DtUpdateRequest+setUpdate ops = update (setUpdateOp ops)++mapUpdate :: [CRDT.MapOp]+ -> BucketType -> Bucket -> Key+ -> DtUpdate.DtUpdateRequest+mapUpdate ops = update (mapUpdateOp ops)+++update :: DtOp.DtOp -> BucketType -> Bucket -> Key -> DtUpdate.DtUpdateRequest+update op t b k = DtUpdate.DtUpdateRequest {+ DtUpdate.bucket = b,+ DtUpdate.key = Just k,+ DtUpdate.type' = t,+ DtUpdate.context = Nothing,+ DtUpdate.op = op,+ DtUpdate.w = Nothing,+ DtUpdate.dw = Nothing,+ DtUpdate.pw = Nothing,+ DtUpdate.return_body = Nothing,+ DtUpdate.timeout = Nothing,+ DtUpdate.sloppy_quorum = Nothing,+ DtUpdate.n_val = Nothing,+ DtUpdate.include_context = Nothing+ }++get :: ByteString -> ByteString -> ByteString -> DtFetch.DtFetchRequest+get t b k = DtFetch.DtFetchRequest {+ DtFetch.bucket = b,+ DtFetch.key = k,+ DtFetch.type' = t,+ DtFetch.r = Nothing,+ DtFetch.pr = Nothing,+ DtFetch.basic_quorum = Nothing,+ DtFetch.notfound_ok = Nothing,+ DtFetch.timeout = Nothing,+ DtFetch.sloppy_quorum = Nothing,+ DtFetch.n_val = Nothing,+ DtFetch.include_context = Nothing+ }
+ src/Network/Riak/CRDT/Response.hs view
@@ -0,0 +1,56 @@+-- | module: Network.Riak.CRDT.Response+-- copyright: (c) 2016 Sentenai+-- author: Antonio Nikishaev <me@lelf.lu>+-- license: Apache+-- +{-# LANGUAGE RecordWildCards #-}+module Network.Riak.CRDT.Response (get) where++import Control.Applicative ((<$>))+import qualified Data.Sequence as Seq+import qualified Data.Map as Map+import Data.Foldable (toList)+import Data.Traversable+import Data.Maybe+import Network.Riak.Protocol.DtFetchResponse (DtFetchResponse,value,type')+import Network.Riak.Protocol.DtFetchResponse.DataType (DataType(..))+import Network.Riak.Protocol.DtValue (counter_value,set_value,map_value)++import qualified Network.Riak.Protocol.MapEntry as M (MapEntry(..))+import qualified Network.Riak.Protocol.MapField as M+import qualified Network.Riak.Protocol.MapField.MapFieldType as M++import Network.Riak.CRDT.Types as CRDT++get :: DtFetchResponse -> Maybe CRDT.DataType+get resp = case type' resp of+ COUNTER ->+ DTCounter . Counter <$> (counter_value =<< value resp)+ SET ->+ DTSet . setFromSeq . set_value <$> value resp+ MAP ->+ DTMap . deconstructMap . map_value <$> value resp++deconstructMap :: Seq.Seq M.MapEntry -> Map+deconstructMap = Map . Map.fromList . catMaybes . map f . toList++f :: M.MapEntry -> Maybe (MapField, MapEntry)+f (M.MapEntry{..}) = sequenceA (MapField t name, v)+ where M.MapField{..} = field+ t :: MapEntryTag+ t = typeToTag type'+ v :: Maybe MapEntry+ v = case type' of+ M.COUNTER -> MapCounter . Counter <$> counter_value+ M.SET -> Just . MapSet . setFromSeq $ set_value+ M.REGISTER -> MapRegister . Register <$> register_value+ M.FLAG -> MapFlag . Flag <$> flag_value+ M.MAP -> Just . MapMap . deconstructMap $ map_value++typeToTag :: M.MapFieldType -> MapEntryTag+typeToTag M.COUNTER = MapCounterTag+typeToTag M.SET = MapSetTag+typeToTag M.REGISTER = MapRegisterTag+typeToTag M.FLAG = MapFlagTag+typeToTag M.MAP = MapMapTag+
+ src/Network/Riak/CRDT/Riak.hs view
@@ -0,0 +1,35 @@+-- | module: Network.Riak.CRDT.Riak+-- copyright: (c) 2016 Sentenai+-- author: Antonio Nikishaev <me@lelf.lu>+-- license: Apache+--+-- CRDT operations+module Network.Riak.CRDT.Riak where++import Control.Applicative+import qualified Network.Riak.CRDT.Types as CRDT+import qualified Network.Riak.Connection as Conn+import Network.Riak.Types++import qualified Network.Riak.CRDT.Request as Req+import qualified Network.Riak.CRDT.Response as Resp+++counterSendUpdate :: Connection -> BucketType -> Bucket -> Key+ -> [CRDT.CounterOp] -> IO ()+counterSendUpdate conn t b k ops = Conn.exchange_ conn (Req.counterUpdate ops t b k)+++setSendUpdate :: Connection -> BucketType -> Bucket -> Key+ -> [CRDT.SetOp] -> IO ()+setSendUpdate conn t b k ops = Conn.exchange_ conn (Req.setUpdate ops t b k)+++mapSendUpdate :: Connection -> BucketType -> Bucket -> Key+ -> [CRDT.MapOp] -> IO ()+mapSendUpdate conn t b k ops = Conn.exchange_ conn (Req.mapUpdate ops t b k)+++get :: Connection -> BucketType -> Bucket -> Key+ -> IO (Maybe CRDT.DataType)+get conn t b k = Resp.get <$> Conn.exchange conn (Req.get t b k)
+ src/Network/Riak/CRDT/Types.hs view
@@ -0,0 +1,284 @@+-- | module: Network.Riak.CRDT.Types+-- copyright: (c) 2016 Sentenai+-- author: Antonio Nikishaev <me@lelf.lu>+-- license: Apache+-- +-- Haskell-side view of CRDT+-- +{-# LANGUAGE OverloadedStrings, PatternGuards, GeneralizedNewtypeDeriving, DeriveGeneric #-}+++module Network.Riak.CRDT.Types (+ -- * Types+ DataType(..),+ -- ** Counters+ Counter(..), Count,+ -- *** Modification+ CounterOp(..),+ -- ** Sets+ Set(..),+ -- *** Modification+ SetOp(..),+ -- ** Maps+ Map(..), MapContent,+ MapField(..),+ MapEntry(..),+ -- *** Inspection+ xlookup,+ -- *** Modification+ MapOp(..), MapPath(..), MapValueOp(..), mapUpdate, (-/),+ -- ** Registers+ Register(..),+ -- *** Modification+ RegisterOp(..),+ -- ** Flags+ Flag(..),+ -- *** Modification+ FlagOp(..),+ -- * Misc+ NonEmpty(..), mapEntryTag, setFromSeq, MapEntryTag(..))+ where+++import qualified Data.Map.Strict as M+import qualified Data.Set as S+import qualified Data.Sequence as Seq+import qualified Data.Foldable as F+import Data.ByteString.Lazy (ByteString)+import Data.Int (Int64)+import Data.List.NonEmpty+import Data.Semigroup+import Data.Default.Class+import Control.DeepSeq (NFData)+import GHC.Generics (Generic)+import Data.String+++-- | CRDT Map is indexed by MapField, which is a name tagged by a type+-- (there may be different entries with the same name, but different+-- types)+data MapField = MapField MapEntryTag ByteString deriving (Eq,Ord,Show,Generic)++instance NFData MapField++-- | CRDT Map is a Data.Map indexed by 'MapField' and holding+-- 'MapEntry'.+-- +-- Maps are specials in a way that they can additionally+-- hold 'Flag's, 'Register's, and most importantly, other 'Map's.+newtype Map = Map MapContent deriving (Eq,Show,Generic)++instance NFData Map++type MapContent = M.Map MapField MapEntry++instance Default Map where+ def = Map M.empty++data MapEntryTag = MapCounterTag+ | MapSetTag+ | MapRegisterTag+ | MapFlagTag+ | MapMapTag+ deriving (Eq,Ord,Show,Generic)++instance NFData MapEntryTag++-- | CRDT Map holds values of type 'MapEntry'+data MapEntry = MapCounter !Counter+ | MapSet !Set+ | MapRegister !Register+ | MapFlag !Flag+ | MapMap !Map+ deriving (Eq,Show,Generic)++instance NFData MapEntry+++mapEntryTag :: MapValueOp -> MapEntryTag+mapEntryTag MapCounterOp{} = MapCounterTag+mapEntryTag MapSetOp{} = MapSetTag+mapEntryTag MapRegisterOp{} = MapRegisterTag+mapEntryTag MapFlagOp{} = MapFlagTag+mapEntryTag MapMapOp{} = MapMapTag+++-- | Selector (“xpath”) inside 'Map'+newtype MapPath = MapPath (NonEmpty ByteString) deriving (Eq,Show)+++-- | map operations+-- It's easier to use 'mapUpdate':+-- +-- >>> "x" -/ "y" -/ "z" `mapUpdate` SetAdd "elem"+-- MapUpdate (MapPath ("x" :| ["y","z"])) (MapCounterOp (CounterInc 1))+data MapOp = MapRemove MapField -- ^ remove value in map+ | MapUpdate MapPath MapValueOp -- ^ update value on path by operation+ deriving (Eq,Show)+++-- | Polymprhic version of MapOp for nicer syntax+data MapOp_ op = MapRemove_ MapField+ | MapUpdate_ MapPath op+ deriving Show+++instance IsString MapPath where+ fromString s = MapPath (fromString s :| [])++(-/) :: ByteString -> MapPath -> MapPath+e -/ (MapPath p) = MapPath (e <| p)++infixr 6 -/++class IsMapOp op where toValueOp :: op -> MapValueOp+instance IsMapOp CounterOp where toValueOp = MapCounterOp+instance IsMapOp FlagOp where toValueOp = MapFlagOp+instance IsMapOp RegisterOp where toValueOp = MapRegisterOp+instance IsMapOp SetOp where toValueOp = MapSetOp+++mapUpdate :: IsMapOp o => MapPath -> o -> MapOp+p `mapUpdate` op = MapUpdate p (toValueOp op)++infixr 5 `mapUpdate`++++-- | Lookup a value of a given 'MapEntryTag' type on a given 'MapPath'+-- inside a map+-- +-- >>> lookup ("a" -/ "b") MapFlagTag $ { "a"/Map: { "b"/Flag: Flag False } } -- pseudo+-- Just (MapFlag (Flag False))+xlookup :: MapPath -> MapEntryTag -> Map -> Maybe MapEntry+xlookup (MapPath (e :| [])) tag (Map m) = M.lookup (MapField tag e) m+xlookup (MapPath (e :| (r:rs))) tag (Map m)+ | Just (MapMap m') <- inner = xlookup (MapPath (r :| rs)) tag m'+ | otherwise = Nothing+ where inner = M.lookup (MapField MapMapTag e) m+++++-- | Registers can be set to a value+-- +-- >>> RegisterSet "foo"+data RegisterOp = RegisterSet !ByteString deriving (Eq,Show)++-- | Flags can be enabled / disabled+-- +-- >>> FlagSet True+data FlagOp = FlagSet !Bool deriving (Eq,Show)++-- | Flags can only be held as a 'Map' element.+-- +-- Flag can be set or unset+-- +-- >>> Flag False+newtype Flag = Flag Bool deriving (Eq,Ord,Show,Generic)++instance NFData Flag++-- | Last-wins monoid for 'Flag'+instance Monoid Flag where+ mempty = Flag False+ mappend = (<>)++-- | Last-wins semigroup for 'Flag'+instance Semigroup Flag where+ a <> b = getLast (Last a <> Last b)++instance Default Flag where+ def = mempty++-- | Registers can only be held as a 'Map' element.+-- +-- Register holds a 'ByteString'.+newtype Register = Register ByteString deriving (Eq,Show,Generic)++instance NFData Register++-- | Last-wins monoid for 'Register'+instance Monoid Register where+ mempty = Register ""+ mappend = (<>)++instance Semigroup Register where+ a <> b = getLast (Last a <> Last b)++instance Default Register where+ def = mempty++++-- | Operations on map values+data MapValueOp = MapCounterOp !CounterOp+ | MapSetOp !SetOp+ | MapRegisterOp !RegisterOp+ | MapFlagOp !FlagOp+ | MapMapOp !MapOp+ deriving (Eq,Show)+++-- | CRDT ADT.+-- +-- 'Network.Riak.CRDT.Riak.get' operations return value of this type+data DataType = DTCounter Counter+ | DTSet Set+ | DTMap Map+ deriving (Eq,Show,Generic)++instance NFData DataType++-- | CRDT Set is a Data.Set+-- +-- >>> Set (Data.Set.fromList ["foo","bar"])+newtype Set = Set (S.Set ByteString) deriving (Eq,Ord,Show,Generic,Monoid)++instance NFData Set++instance Semigroup Set where+ Set a <> Set b = Set (a <> b)++instance Default Set where+ def = Set mempty++-- | CRDT Set operations+data SetOp = SetAdd ByteString -- ^ add element to the set+ -- + -- >>> SetAdd "foo"+ | SetRemove ByteString -- ^ remove element from the set+ -- + -- >>> SetRemove "bar"+ deriving (Eq,Show)++setFromSeq :: Seq.Seq ByteString -> Set+setFromSeq = Set . S.fromList . F.toList++-- | CRDT Counter hold a integer 'Count'+-- +-- >>> Counter 42+newtype Counter = Counter Count deriving (Eq,Ord,Num,Show,Generic)+type Count = Int64++instance NFData Counter++instance Semigroup Counter where+ Counter a <> Counter b = Counter . getSum $ Sum a <> Sum b++instance Monoid Counter where+ mempty = Counter 0+ mappend = (<>)++instance Default Counter where+ def = mempty++-- | Counters can be incremented/decremented+-- +-- >>> CounterInc 1+data CounterOp = CounterInc !Count deriving (Eq,Show)++instance Monoid CounterOp where+ mempty = CounterInc 0+ CounterInc x `mappend` CounterInc y = CounterInc . getSum $ Sum x <> Sum y+
src/Network/Riak/Connection/Internal.hs view
@@ -53,7 +53,6 @@ import Network.Riak.Types.Internal hiding (MessageTag(..)) import Network.Socket as Socket import Numeric (showHex)-import System.Mem.Weak (addFinalizer) import System.Random (randomIO) import Text.ProtocolBuffers (messageGetM, messagePutM, messageSize) import Text.ProtocolBuffers.Get (Get, Result(..), getWord32be, runGet)@@ -114,7 +113,6 @@ Socket.connect sock (addrAddress ai) buf <- newIORef L.empty let conn = Connection sock client buf- addFinalizer conn $ sClose sock -- Data.Pool doesn't guarantee our disconnect gets called... setClientID conn clientID return conn
src/Network/Riak/JSON.hs view
@@ -63,11 +63,11 @@ -- Choosing among them is your responsibility. get :: (FromJSON c, ToJSON c) => Connection -> Bucket -> Key -> R -> IO (Maybe ([c], VClock))-get conn bucket key r = fmap convert <$> V.get conn bucket key r+get conn bucket' key' r = fmap convert <$> V.get conn bucket' key' r getMany :: (FromJSON c, ToJSON c) => Connection -> Bucket -> [Key] -> R -> IO [Maybe ([c], VClock)]-getMany conn bucket ks r = map (fmap convert) <$> V.getMany conn bucket ks r+getMany conn bucket' ks r = map (fmap convert) <$> V.getMany conn bucket' ks r -- | Store a single value. This may return multiple conflicting -- siblings. Choosing among them, and storing a new value, is your@@ -80,16 +80,16 @@ put :: (FromJSON c, ToJSON c) => Connection -> Bucket -> Key -> Maybe VClock -> c -> W -> DW -> IO ([c], VClock)-put conn bucket key mvclock val w dw =- convert <$> V.put conn bucket key mvclock (json val) w dw+put conn bucket' key' mvclock val w dw =+ convert <$> V.put conn bucket' key' mvclock (json val) w dw -- | Store a single value indexed. putIndexed :: (FromJSON c, ToJSON c) => Connection -> Bucket -> Key -> [IndexValue] -> Maybe VClock -> c -> W -> DW -> IO ([c], VClock)-putIndexed conn bucket key ixs mvclock val w dw =- convert <$> V.putIndexed conn bucket key ixs mvclock (json val) w dw+putIndexed conn bucket' key' ixs mvclock val w dw =+ convert <$> V.putIndexed conn bucket' key' ixs mvclock (json val) w dw -- | Store a single value, without the possibility of conflict -- resolution.@@ -101,8 +101,8 @@ put_ :: (FromJSON c, ToJSON c) => Connection -> Bucket -> Key -> Maybe VClock -> c -> W -> DW -> IO ()-put_ conn bucket key mvclock val w dw =- V.put_ conn bucket key mvclock (json val) w dw+put_ conn bucket' key' mvclock val w dw =+ V.put_ conn bucket' key' mvclock (json val) w dw -- | Store many values. This may return multiple conflicting siblings -- for each value stored. Choosing among them, and storing a new@@ -115,8 +115,8 @@ putMany :: (FromJSON c, ToJSON c) => Connection -> Bucket -> [(Key, Maybe VClock, c)] -> W -> DW -> IO [([c], VClock)]-putMany conn bucket puts w dw =- map convert <$> V.putMany conn bucket (map f puts) w dw+putMany conn bucket' puts w dw =+ map convert <$> V.putMany conn bucket' (map f puts) w dw where f (k,v,c) = (k,v,json c) -- | Store many values, without the possibility of conflict@@ -129,7 +129,7 @@ putMany_ :: (FromJSON c, ToJSON c) => Connection -> Bucket -> [(Key, Maybe VClock, c)] -> W -> DW -> IO ()-putMany_ conn bucket puts w dw = V.putMany_ conn bucket (map f puts) w dw+putMany_ conn bucket' puts w dw = V.putMany_ conn bucket' (map f puts) w dw where f (k,v,c) = (k,v,json c) convert :: ([JSON a], VClock) -> ([a], VClock)
src/Network/Riak/JSON/Resolvable.hs view
@@ -115,8 +115,8 @@ -> Maybe VClock -> c -> W -> DW -> IO (c, VClock) putIndexed c b k ixs vc cont w' dw' =- R.put (\conn bucket key mvclock val w dw ->- J.putIndexed conn bucket key ixs mvclock val w dw)+ R.put (\conn bucket' key' mvclock val w dw ->+ J.putIndexed conn bucket' key' ixs mvclock val w dw) c b k vc cont w' dw' {-# INLINE putIndexed #-}
src/Network/Riak/Request.hs view
@@ -41,9 +41,12 @@ , getBucket , SetBucket.SetBucketRequest , setBucket+ , getBucketType -- * Map/reduce , MapReduceRequest , mapReduce+ , search+ , getIndex ) where #if __GLASGOW_HASKELL__ < 710@@ -51,14 +54,14 @@ #endif import qualified Data.ByteString.Char8 as B8 import Data.Monoid-import Network.Riak.Protocol.BucketProps hiding (r,rw)+import Network.Riak.Protocol.BucketProps (BucketProps) import Network.Riak.Protocol.Content import Network.Riak.Protocol.GetClientIDRequest import Network.Riak.Protocol.GetServerInfoRequest import Network.Riak.Protocol.ListBucketsRequest import Network.Riak.Protocol.MapReduceRequest import Network.Riak.Protocol.PingRequest-import Network.Riak.Types.Internal hiding (MessageTag(..))+import Network.Riak.Types.Internal hiding (MessageTag(..),bucket,key) import Network.Riak.Escape (escape) import qualified Network.Riak.Protocol.DeleteRequest as Del import qualified Network.Riak.Protocol.Link as Link@@ -69,11 +72,20 @@ import qualified Network.Riak.Protocol.ListKeysRequest as Keys import qualified Network.Riak.Protocol.PutRequest as Put import qualified Network.Riak.Protocol.SetBucketRequest as SetBucket+import qualified Network.Riak.Protocol.GetBucketTypeRequest as GetBucketType+import qualified Network.Riak.Protocol.SearchQueryRequest as SearchQueryRequest+import qualified Network.Riak.Protocol.YzIndexGetRequest as YzIndex -- | Create a ping request. ping :: PingRequest ping = PingRequest {-# INLINE ping #-}+{-+-- | Create a dtUpdate request.+dtUpdate :: DtUpdate.DtUpdateRequest+dtUpdate = DtUpdate.DtUpdateRequest+{-# INLINE dtUpdate #-}+-} -- | Create a client-ID request. getClientID :: GetClientIDRequest@@ -99,6 +111,7 @@ , Get.timeout = Nothing , Get.sloppy_quorum = Nothing , Get.n_val = Nothing+ , Get.type' = Nothing } {-# INLINE get #-} @@ -134,7 +147,11 @@ , Index.stream = Nothing , Index.max_results = Nothing , Index.continuation = Nothing- , Index.timeout = Nothing }+ , Index.timeout = Nothing+ , Index.type' = Nothing+ , Index.term_regex = Nothing+ , Index.pagination_sort = Nothing+ } -- | Create a put request. The bucket and key names are URL-escaped. -- Any 'Link' values inside the 'Content' are assumed to have been@@ -144,7 +161,7 @@ put bucket key mvclock cont mw mdw returnBody = Put.PutRequest (escape bucket) (Just $ escape key) (fromVClock <$> mvclock) cont (fromQuorum mw) (fromQuorum mdw) (Just returnBody)- Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing+ Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing {-# INLINE put #-} -- | Create a link. The bucket and key names are URL-escaped.@@ -156,30 +173,56 @@ delete :: Bucket -> Key -> RW -> Del.DeleteRequest delete bucket key rw = Del.DeleteRequest (escape bucket) (escape key) (fromQuorum rw) Nothing Nothing Nothing- Nothing Nothing Nothing Nothing Nothing Nothing+ Nothing Nothing Nothing Nothing Nothing Nothing Nothing {-# INLINE delete #-} -- | Create a list-buckets request.-listBuckets :: ListBucketsRequest+listBuckets :: Maybe BucketType -> ListBucketsRequest listBuckets = ListBucketsRequest Nothing Nothing {-# INLINE listBuckets #-} --- | Create a list-keys request. The bucket name is URL-escaped.-listKeys :: Bucket -> Keys.ListKeysRequest-listKeys b = Keys.ListKeysRequest (escape b) Nothing+-- | Create a list-keys request. The bucket type and name are URL-escaped.+listKeys :: Maybe BucketType -> Bucket -> Keys.ListKeysRequest+listKeys t b = Keys.ListKeysRequest (escape b) Nothing (escape <$> t) {-# INLINE listKeys #-} --- | Create a get-bucket request. The bucket name is URL-escaped.-getBucket :: Bucket -> GetBucket.GetBucketRequest-getBucket = GetBucket.GetBucketRequest . escape+-- | Create a get-bucket request. The bucket type and name are URL-escaped.+getBucket :: Maybe BucketType -> Bucket -> GetBucket.GetBucketRequest+getBucket t b = GetBucket.GetBucketRequest (escape b) (escape <$> t) {-# INLINE getBucket #-} --- | Create a set-bucket request. The bucket name is URL-escaped.-setBucket :: Bucket -> BucketProps -> SetBucket.SetBucketRequest-setBucket = SetBucket.SetBucketRequest . escape+-- | Create a set-bucket request. The bucket type and name are URL-escaped.+setBucket :: Maybe BucketType -> Bucket -> BucketProps -> SetBucket.SetBucketRequest+setBucket t b ps = SetBucket.SetBucketRequest (escape b) ps (escape <$> t) {-# INLINE setBucket #-} +-- | Create a get-bucket-type request. The bucket type is URL-escaped.+getBucketType :: BucketType -> GetBucketType.GetBucketTypeRequest+getBucketType t = GetBucketType.GetBucketTypeRequest (escape t)+ -- | Create a map-reduce request. mapReduce :: Job -> MapReduceRequest mapReduce (JSON bs) = MapReduceRequest bs "application/json" mapReduce (Erlang bs) = MapReduceRequest bs "application/x-erlang-binary"++-- | Create a search request+search :: SearchQuery -> Index -> SearchQueryRequest.SearchQueryRequest+search q ix = SearchQueryRequest.SearchQueryRequest {+ SearchQueryRequest.q = q,+ SearchQueryRequest.index = escape ix,+ SearchQueryRequest.rows = Nothing,+ SearchQueryRequest.start = Nothing,+ SearchQueryRequest.sort = Nothing,+ SearchQueryRequest.filter = Nothing,+ SearchQueryRequest.df = Nothing,+ SearchQueryRequest.op = Nothing,+ SearchQueryRequest.fl = mempty,+ SearchQueryRequest.presort = Nothing+ }++getIndex :: Maybe Index -> YzIndex.YzIndexGetRequest+getIndex = YzIndex.YzIndexGetRequest++++
src/Network/Riak/Resolvable/Internal.hs view
@@ -102,8 +102,8 @@ get :: (Resolvable a) => Get a -> (Connection -> Bucket -> Key -> R -> IO (Maybe (a, VClock)))-get doGet conn bucket key r =- fmap (first resolveMany) `fmap` doGet conn bucket key r+get doGet conn bucket' key' r =+ fmap (first resolveMany) `fmap` doGet conn bucket' key' r {-# INLINE get #-} getMany :: (Resolvable a) =>@@ -128,11 +128,11 @@ put :: (Resolvable a) => Put a -> Connection -> Bucket -> Key -> Maybe VClock -> a -> W -> DW -> IO (a, VClock)-put doPut conn bucket key mvclock0 val0 w dw = do+put doPut conn bucket' key' mvclock0 val0 w dw = do let go !i val mvclock | i == maxRetries = throwIO RetriesExceeded | otherwise = do- (xs, vclock) <- doPut conn bucket key mvclock val w dw+ (xs, vclock) <- doPut conn bucket' key' mvclock val w dw case xs of [x] | i > 0 || isJust mvclock -> return (x, vclock) (_:_) -> do debugValues "put" "conflict" xs@@ -152,27 +152,27 @@ -> IO ([a], VClock)) -> Connection -> Bucket -> Key -> Maybe VClock -> a -> W -> DW -> IO ()-put_ doPut conn bucket key mvclock0 val0 w dw =- put doPut conn bucket key mvclock0 val0 w dw >> return ()+put_ doPut conn bucket' key' mvclock0 val0 w dw =+ put doPut conn bucket' key' mvclock0 val0 w dw >> return () {-# INLINE put_ #-} modify :: (MonadIO m, Resolvable a) => Get a -> Put a -> Connection -> Bucket -> Key -> R -> W -> DW -> (Maybe a -> m (a,b)) -> m (a,b)-modify doGet doPut conn bucket key r w dw act = do- a0 <- liftIO $ get doGet conn bucket key r+modify doGet doPut conn bucket' key' r w dw act = do+ a0 <- liftIO $ get doGet conn bucket' key' r (a,b) <- act (fst <$> a0)- (a',_) <- liftIO $ put doPut conn bucket key (snd <$> a0) a w dw+ (a',_) <- liftIO $ put doPut conn bucket' key' (snd <$> a0) a w dw return (a',b) {-# INLINE modify #-} modify_ :: (MonadIO m, Resolvable a) => Get a -> Put a -> Connection -> Bucket -> Key -> R -> W -> DW -> (Maybe a -> m a) -> m a-modify_ doGet doPut conn bucket key r w dw act = do- a0 <- liftIO $ get doGet conn bucket key r+modify_ doGet doPut conn bucket' key' r w dw act = do+ a0 <- liftIO $ get doGet conn bucket' key' r a <- act (fst <$> a0)- liftIO $ fst <$> put doPut conn bucket key (snd <$> a0) a w dw+ liftIO $ fst <$> put doPut conn bucket' key' (snd <$> a0) a w dw {-# INLINE modify_ #-} putMany :: (Resolvable a) =>@@ -180,13 +180,13 @@ -> IO [([a], VClock)]) -> Connection -> Bucket -> [(Key, Maybe VClock, a)] -> W -> DW -> IO [(a, VClock)]-putMany doPut conn bucket puts0 w dw = go (0::Int) [] . zip [(0::Int)..] $ puts0+putMany doPut conn bucket' puts0 w dw = go (0::Int) [] . zip [(0::Int)..] $ puts0 where go _ acc [] = return . map snd . sortBy (compare `on` fst) $ acc go !i acc puts | i == maxRetries = throwIO RetriesExceeded | otherwise = do- rs <- doPut conn bucket (map snd puts) w dw+ rs <- doPut conn bucket' (map snd puts) w dw let (conflicts, ok) = partitionEithers $ zipWith mush puts rs unless (null conflicts) $ debugValues "putMany" "conflicts" conflicts@@ -203,8 +203,8 @@ (Connection -> Bucket -> [(Key, Maybe VClock, a)] -> W -> DW -> IO [([a], VClock)]) -> Connection -> Bucket -> [(Key, Maybe VClock, a)] -> W -> DW -> IO ()-putMany_ doPut conn bucket puts0 w dw =- putMany doPut conn bucket puts0 w dw >> return ()+putMany_ doPut conn bucket' puts0 w dw =+ putMany doPut conn bucket' puts0 w dw >> return () {-# INLINE putMany_ #-} resolveMany' :: (Resolvable a) => a -> [a] -> a
src/Network/Riak/Response.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE RecordWildCards, CPP #-}+{-# LANGUAGE RecordWildCards, CPP, OverloadedStrings #-} -- | -- Module: Network.Riak.Request@@ -24,25 +24,36 @@ , listBuckets , getBucket , unescapeLinks+ , search+ , getIndex ) where #if __GLASGOW_HASKELL__ < 710 import Control.Applicative ((<$>)) #endif-import Data.Maybe (fromMaybe) import Network.Riak.Escape (unescape)-import Network.Riak.Protocol.BucketProps+import Network.Riak.Protocol.BucketProps (BucketProps) import Network.Riak.Protocol.Content import Network.Riak.Protocol.GetBucketResponse import Network.Riak.Protocol.GetClientIDResponse import Network.Riak.Protocol.GetResponse-import Network.Riak.Protocol.Link import Network.Riak.Protocol.ListBucketsResponse import Network.Riak.Protocol.PutResponse+import Network.Riak.Protocol.SearchQueryResponse+import Network.Riak.Protocol.SearchDoc+import qualified Network.Riak.Protocol.YzIndexGetResponse as Yz import Network.Riak.Types.Internal hiding (MessageTag(..)) import qualified Network.Riak.Protocol.Link as Link+import qualified Network.Riak.Protocol.Pair as Pair+ import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString.Lazy.Char8 as LC import qualified Data.Sequence as Seq+import qualified Data.Map.Strict as M+import Data.Maybe (fromMaybe)+import Data.Semigroup+import Control.Arrow ((&&&))+import Data.Foldable (toList) getClientID :: GetClientIDResponse -> ClientID getClientID = client_id@@ -76,5 +87,31 @@ -- 'Content' value. unescapeLinks :: Content -> Content unescapeLinks c = c { links = go <$> links c }- where go l = l { bucket = unescape <$> bucket l+ where go l = l { Link.bucket = unescape <$> Link.bucket l , Link.key = unescape <$> Link.key l }++search :: SearchQueryResponse -> [SearchResult]+search = map toSearchResult . toList . docs++toSearchResult :: SearchDoc -> SearchResult+toSearchResult r = SearchResult {+ bucketType = field "_yz_rt",+ bucket = field "_yz_rb",+ key = field "_yz_rk",+ score = fmap (read . LC.unpack) =<< M.lookup "score" info+ }+ where+ info :: M.Map L.ByteString (Maybe L.ByteString)+ info = M.fromList . map (Pair.key &&& Pair.value) . toList . fields $ r++ field :: L.ByteString -> L.ByteString+ field name+ = maybe (unexpected $ "field " <> show name <> " has empty value") id+ . maybe (unexpected $ "no " <> show name <> " field") id+ . M.lookup name $ info++ unexpected = unexError "Network.Riak.Response" "search"+++getIndex :: Yz.YzIndexGetResponse -> [IndexInfo]+getIndex = toList . Yz.index
+ src/Network/Riak/Search.hs view
@@ -0,0 +1,28 @@+-- | module: Network.Riak.Search+-- copyright: (c) 2016 Sentenai+-- author: Antonio Nikishaev <me@lelf.lu>+-- license: Apache+-- +-- Solr search+-- +-- http://docs.basho.com/riak/2.1.3/dev/using/search/++module Network.Riak.Search where++import Network.Riak.Connection.Internal+import Network.Riak.Types.Internal+import qualified Network.Riak.Request as Req+import qualified Network.Riak.Response as Resp+import Control.Applicative++-- | Get an index info for @Just index@, or get all indexes for+-- @Nothing@.+getIndex :: Connection -> Maybe Index -> IO [IndexInfo]+getIndex conn ix = Resp.getIndex <$> exchange conn (Req.getIndex ix)+++-- | Search by raw 'SearchQuery' request (a bytestring) using an+-- index.+searchRaw :: Connection -> SearchQuery -> Index -> IO [SearchResult]+searchRaw conn q ix = Resp.search <$> exchange conn (Req.search q ix)+
src/Network/Riak/Tag.hs view
@@ -18,9 +18,14 @@ ) where import Data.Binary.Put (Put, putWord8)+import Data.Word (Word8)+import qualified Data.HashMap.Strict as HM+import Control.Applicative+import Data.Tuple (swap) import Network.Riak.Protocol.DeleteRequest import Network.Riak.Protocol.ErrorResponse import Network.Riak.Protocol.GetBucketRequest+import Network.Riak.Protocol.GetBucketTypeRequest import Network.Riak.Protocol.GetBucketResponse import Network.Riak.Protocol.GetClientIDRequest import Network.Riak.Protocol.GetClientIDResponse@@ -41,6 +46,14 @@ import Network.Riak.Protocol.ServerInfo import Network.Riak.Protocol.SetBucketRequest import Network.Riak.Protocol.SetClientIDRequest+import Network.Riak.Protocol.DtFetchRequest+import Network.Riak.Protocol.DtFetchResponse+import Network.Riak.Protocol.DtUpdateRequest+import Network.Riak.Protocol.DtUpdateResponse+import Network.Riak.Protocol.SearchQueryRequest+import Network.Riak.Protocol.SearchQueryResponse+import Network.Riak.Protocol.YzIndexGetRequest+import Network.Riak.Protocol.YzIndexGetResponse import Network.Riak.Types.Internal as Types import Text.ProtocolBuffers.Get (Get, getWord8) @@ -208,6 +221,14 @@ expectedResponse _ = Types.SetBucketResponse {-# INLINE expectedResponse #-} +instance Request GetBucketTypeRequest where+ expectedResponse _ = Types.GetBucketResponse++instance Tagged GetBucketTypeRequest where+ messageTag _ = Types.GetBucketTypeRequest++instance Exchange GetBucketTypeRequest GetBucketResponse+ instance Tagged MapReduceRequest where messageTag _ = Types.MapReduceRequest {-# INLINE messageTag #-}@@ -220,21 +241,157 @@ messageTag _ = Types.MapReduceResponse {-# INLINE messageTag #-} + instance Response MapReduce instance Exchange MapReduceRequest MapReduce +instance Tagged DtFetchRequest where+ messageTag _ = Types.DtFetchRequest+ {-# INLINE messageTag #-}++instance Tagged DtFetchResponse where+ messageTag _ = Types.DtFetchResponse+ {-# INLINE messageTag #-}++instance Request DtFetchRequest where+ expectedResponse _ = Types.DtFetchResponse+ {-# INLINE expectedResponse #-}++instance Response DtFetchResponse++instance Exchange DtFetchRequest DtFetchResponse++instance Tagged DtUpdateRequest where+ messageTag _ = Types.DtUpdateRequest+ {-# INLINE messageTag #-}++instance Tagged DtUpdateResponse where+ messageTag _ = Types.DtUpdateResponse+ {-# INLINE messageTag #-}++instance Request DtUpdateRequest where+ expectedResponse _ = Types.DtUpdateResponse+ {-# INLINE expectedResponse #-}++instance Response DtUpdateResponse++instance Exchange DtUpdateRequest DtUpdateResponse++instance Tagged SearchQueryRequest where+ messageTag _ = Types.SearchQueryRequest+ {-# INLINE messageTag #-}++instance Request SearchQueryRequest where+ expectedResponse _ = Types.SearchQueryResponse+ {-# INLINE expectedResponse #-}++instance Tagged SearchQueryResponse where+ messageTag _ = Types.SearchQueryResponse+ {-# INLINE messageTag #-}++instance Response SearchQueryResponse++instance Exchange SearchQueryRequest SearchQueryResponse++instance Tagged YzIndexGetRequest where+ messageTag _ = Types.YokozunaIndexGetRequest++instance Request YzIndexGetRequest where+ expectedResponse _ = Types.YokozunaIndexGetResponse++instance Tagged YzIndexGetResponse where+ messageTag _ = Types.YokozunaIndexGetResponse++instance Response YzIndexGetResponse++instance Exchange YzIndexGetRequest YzIndexGetResponse+ putTag :: MessageTag -> Put-putTag = putWord8 . fromIntegral . fromEnum+putTag m = putWord8 $ message2code HM.! m {-# INLINE putTag #-} getTag :: Get MessageTag getTag = do n <- getWord8- if n > 26- then moduleError "getTag" $ "invalid riak message code: " ++ show n- else return . toEnum . fromIntegral $ n+ maybe (err n) pure $ HM.lookup n code2message+ where+ err n = moduleError "getTag" $ "invalid riak message code: " ++ show n {-# INLINE getTag #-} moduleError :: String -> String -> a moduleError = netError "Network.Riak.Tag"+++code2message :: HM.HashMap Word8 MessageTag+code2message = HM.fromList messageCodes++message2code :: HM.HashMap MessageTag Word8+message2code = HM.fromList . map swap $ messageCodes++messageCodes :: [(Word8, MessageTag)]+messageCodes = [+ -- From riak-2.1.3/deps/riak_pb/src/riak_pb_messages.csv+ --+ -- This is a list of all known riak messages (with appropriate+ -- message codes). Most of them are described at+ -- http://docs.basho.com/riak/2.1.3/dev/references/protocol-buffers/+ --+ -- Commented ones are messages we don't use/support yet.+ (0, Types.ErrorResponse),+ (1, Types.PingRequest),+ (2, Types.PingResponse),+ (3, Types.GetClientIDResponse),+ (4, Types.GetClientIDResponse),+ (5, Types.SetClientIDRequest),+ (6, Types.SetClientIDResponse),+ (7, Types.GetServerInfoRequest),+ (8, Types.GetServerInfoResponse),+ (9, Types.GetRequest),+ (10, Types.GetResponse),+ (11, Types.PutRequest),+ (12, Types.PutResponse),+ (13, Types.DeleteRequest),+ (14, Types.DeleteResponse),+ (15, Types.ListBucketsRequest),+ (16, Types.ListBucketsResponse),+ (17, Types.ListKeysRequest),+ (18, Types.ListKeysResponse),+ (19, Types.GetBucketRequest),+ (20, Types.GetBucketResponse),+ (21, Types.SetBucketRequest),+ (22, Types.SetBucketResponse),+ (23, Types.MapReduceRequest),+ (24, Types.MapReduceResponse),+ (25, Types.IndexRequest),+ (26, Types.IndexResponse),+ (27, Types.SearchQueryRequest),+ (28, Types.SearchQueryResponse),+ -- (29,ResetBucketRequest),+ -- (30,ResetBucketResp),+ (31, Types.GetBucketTypeRequest),+ -- (32,SetBucketTypeReq),+ -- (33,GetBucketKeyPreflistReq),+ -- (34,GetBucketKeyPreflistResp),+ -- (40,CSBucketReq),+ -- (41,CSBucketResp),+ -- (50,CounterUpdateReq),+ -- (51,CounterUpdateResp),+ -- (52,CounterGetReq),+ -- (53,CounterGetResp),+ (54, Types.YokozunaIndexGetRequest),+ (55, Types.YokozunaIndexGetResponse),+ -- (56,YokozunaIndexPutReq),+ -- (57,YokozunaIndexDeleteReq),+ -- (58,YokozunaSchemaGetReq),+ -- (59,YokozunaSchemaGetResp),+ -- (60,YokozunaSchemaPutReq),+ (80, Types.DtFetchRequest),+ (81, Types.DtFetchResponse),+ (82, Types.DtUpdateRequest),+ (83, Types.DtUpdateResponse)+ -- (253,RpbAuthReq),+ -- (254,RpbAuthResp),+ -- (255,RpbStartTls)+ ]+
src/Network/Riak/Types.hs view
@@ -18,6 +18,7 @@ -- * Errors , RiakException(excModule, excFunction, excMessage) -- * Data types+ , BucketType , Bucket , Key , Tag@@ -37,6 +38,7 @@ , Tagged(..) , IndexValue(..) , IndexQuery(..)+ , SearchResult(..) ) where import Network.Riak.Types.Internal
src/Network/Riak/Types/Internal.hs view
@@ -1,5 +1,5 @@ {-# LANGUAGE DeriveDataTypeable, FunctionalDependencies, MultiParamTypeClasses,- RecordWildCards #-}+ RecordWildCards, DeriveGeneric #-} -- | -- Module: Network.Riak.Types.Internal@@ -25,11 +25,15 @@ , unexError -- * Data types , Bucket+ , BucketType , Key , Index , IndexQuery(..) , IndexValue(..) , Tag+ , SearchQuery+ , SearchResult(..)+ , IndexInfo , VClock(..) , Job(..) -- * Quorum management@@ -48,15 +52,19 @@ , Tagged(..) ) where -import Control.Exception (Exception, throw)-import Data.ByteString.Lazy (ByteString)-import Data.Digest.Pure.MD5 (md5)-import Data.IORef (IORef)-import Data.Typeable (Typeable)-import Data.Word (Word32)-import Network.Socket (HostName, ServiceName, Socket)-import Text.ProtocolBuffers (ReflectDescriptor, Wire)+import Control.Exception (Exception, throw)+import Data.ByteString.Lazy (ByteString)+import Data.Digest.Pure.MD5 (md5)+import Data.Hashable (Hashable)+import Data.IORef (IORef)+import Data.Typeable (Typeable)+import Data.Word (Word32)+import GHC.Generics (Generic)+import qualified Network.Riak.Protocol.YzIndex as YzIndex+import Network.Socket (HostName, ServiceName, Socket)+import Text.ProtocolBuffers (ReflectDescriptor, Wire) + -- | A client identifier. This is used by the Riak cluster when -- logging vector clock changes, and should be unique for each client. type ClientID = ByteString@@ -129,6 +137,10 @@ -- replicas, for instance). type Bucket = ByteString +-- | Bucket types is a riak >= 2.0 feature allowing groups of buckets+-- to share configuration details+type BucketType = ByteString+ -- | Keys are unique object identifiers in Riak and are scoped within -- buckets. type Key = ByteString@@ -159,7 +171,24 @@ | Erlang ByteString deriving (Eq, Show, Typeable) --- | An identifier for an inbound or outbound message.+-- | Search request+type SearchQuery = ByteString++-- | Search result score+type Score = Double++-- | Search index info+type IndexInfo = YzIndex.YzIndex++-- | Solr search result+data SearchResult = SearchResult {+ bucketType :: BucketType, -- ^ bucket type+ bucket :: Bucket, -- ^ bucket+ key :: Key, -- ^ key+ score :: Maybe Score -- ^ score, if provided+ } deriving (Eq,Show)++-- | List of (known to us) inbound or outbound message identifiers. data MessageTag = ErrorResponse | PingRequest | PingResponse@@ -183,12 +212,23 @@ | GetBucketResponse | SetBucketRequest | SetBucketResponse+ | GetBucketTypeRequest | MapReduceRequest | MapReduceResponse | IndexRequest | IndexResponse- deriving (Eq, Show, Enum, Typeable)+ | DtFetchRequest+ | DtFetchResponse+ | DtUpdateRequest+ | DtUpdateResponse+ | SearchQueryRequest+ | SearchQueryResponse+ | YokozunaIndexGetRequest+ | YokozunaIndexGetResponse+ deriving (Eq, Show, Generic) +instance Hashable MessageTag+ -- | Messages are tagged. class Tagged msg where messageTag :: msg -> MessageTag -- ^ Retrieve a message's tag.@@ -205,7 +245,7 @@ class (Tagged msg, ReflectDescriptor msg, Show msg, Wire msg) => Response msg class (Request req, Response resp) => Exchange req resp- | req -> resp, resp -> req+ | req -> resp instance (Tagged a, Tagged b) => Tagged (Either a b) where messageTag (Left l) = messageTag l
+ tests/CRDTProperties.hs view
@@ -0,0 +1,309 @@+-- | module: CRDTProperties+-- copyright: (c) 2016 Sentenai+-- author: Antonio Nikishaev <me@lelf.lu>+-- license: Apache+--+{-# LANGUAGE OverloadedStrings, FlexibleContexts, TupleSections, ScopedTypeVariables,+ GADTs, StandaloneDeriving, UndecidableInstances, PatternGuards, MultiParamTypeClasses #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+module CRDTProperties (prop_counters,+ prop_sets,+ prop_maps,+ tests) where++-- |+-- The idea: send arbitrary stream of commands to riak, collect each+-- command output to list :: [Maybe RiakReturnValue]. Then see it we+-- get the same list of results simulating Riak in this module.++import Control.Applicative+import Control.Monad.RWS+import Data.ByteString.Lazy (ByteString)+import Data.Default.Class+import Data.List.NonEmpty+import qualified Data.Map as Map+import Data.Maybe+import Data.Proxy+import qualified Data.Set as Set+import Test.QuickCheck+import Test.QuickCheck.Monadic++import Common+import Test.Tasty+import Test.Tasty.QuickCheck++import qualified Network.Riak.Basic as B+import qualified Network.Riak.CRDT as C++newtype BucketType = BucketType ByteString deriving (Show,Eq,Ord)+newtype Bucket = Bucket ByteString deriving (Show,Eq,Ord)+newtype Key = Key ByteString deriving (Show,Eq,Ord)+newtype Value = Value ByteString deriving Show++class Values a where values :: [a]++-- Not many. We want to hit each multiple times+instance Values BucketType where values = BucketType <$> ["sets","counters","maps"]+instance Values Bucket where values = Bucket <$> ["A","B"]+instance Values Key where values = Key <$> ["a","b","c"]+instance Values Value where values = Value <$> ["1","2","3","4"]++instance Arbitrary BucketType where arbitrary = elements values+instance Arbitrary Bucket where arbitrary = elements values+instance Arbitrary Key where arbitrary = elements values+instance Arbitrary Value where arbitrary = elements values++data Point = Point BucketType Bucket Key deriving (Show,Ord,Eq)+instance Values Point where+ values = [ Point t b k | t <- values, b <- values, k <- values ]++instance Arbitrary Point where arbitrary = elements values++type RiakState = Map.Map Point C.DataType+++-- | observe all current values we care about (instance 'Values') in+-- riak, gather them into a map.+--+-- As it turns out, observeRiak is not quite cheap operation after+-- /types/maps/… are populated. So first argument is proxy for the+-- (only) type we are interested in.+observeRiak :: Action a op => Proxy a -> IO RiakState+observeRiak p = Map.fromList . catMaybes <$> observeRiak' (BucketType $ bucketType p)++observeRiak' :: BucketType -> IO [Maybe (Point, C.DataType)]+observeRiak' bt@(BucketType t_) = withSomeConnection $ \c ->+ sequence [ do r <- C.get c t_ b_ k_+ pure . fmap (p,) $ r+ | b <- values, k <- values,+ let p@(Point _ (Bucket b_) (Key k_)) = Point bt b k+ ]+++-- | We will supply a list of these operations:+--+-- For each Action a op => a,+data Op a op = Get Bucket Key -- ^ we can get a value+ | Update Bucket Key op -- ^ we can update a value++deriving instance (Show op, C.CRDT a o) => Show (Op a op)++class (Show t, C.CRDT t op, Default t) => Action t op where+ -- | bucket type for this type (assumed/hardcoded)+ bucketType :: Proxy t -> ByteString+ -- | extract a value from 'C.DataType', or throw an error+ fromDT :: C.DataType -> t+ -- | pack a value into 'C.DataType'+ toDT :: t -> C.DataType+ -- | a kludge (or two), see 'update': if there's no value at the+ -- moment, having been provieded with an op, will riak create and+ -- operate on a empty value?+ updateCreates :: Proxy t -> Maybe C.DataType -> op -> Bool+ -- | is operation target there?+ targetThere :: Maybe C.DataType -> op -> Bool+++instance Action C.Counter C.CounterOp where+ bucketType _ = "counters"+ fromDT (C.DTCounter c) = c+ fromDT _ = error "expected counter" -- ok for tests+ toDT = C.DTCounter+ updateCreates _ _ _ = True+ targetThere _ _ = True++instance Action C.Set C.SetOp where+ bucketType _ = "sets"+ fromDT (C.DTSet c) = c+ fromDT _ = error "expected set"+ toDT = C.DTSet+ updateCreates _ _ C.SetAdd{} = True+ updateCreates _ _ C.SetRemove{} = False+ targetThere _ _ = True++instance Action C.Map C.MapOp where+ bucketType _ = "maps"+ fromDT (C.DTMap c) = c+ fromDT _ = error "expected map"+ toDT = C.DTMap+ -- Riak will operate on default value inside map if it's not there.+ -- Except while it's SetRemove.+ updateCreates _ _ (C.MapUpdate _ (C.MapSetOp C.SetRemove{})) = False+ updateCreates _ _ _ = True++ targetThere (Just (C.DTMap m)) (C.MapUpdate path _)+ | Just{} <- C.xlookup path C.MapSetTag m = True+ targetThere _ _ = False++-- | Many Arbitrary instances.+-- TODO: 'Generic' arbitrary++instance (Arbitrary a, Arbitrary op) => Arbitrary (Op a op) where+ arbitrary = oneof [+ Get <$> arbitrary <*> arbitrary,+ Update <$> arbitrary <*> arbitrary <*> arbitrary+ ]++instance Arbitrary C.Counter where+ arbitrary = C.Counter <$> arbitrary++instance Arbitrary C.CounterOp where+ arbitrary = C.CounterInc <$> choose (-16,16) -- https://github.com/basho/riak/issues/804++instance Arbitrary C.SetOp where+ arbitrary = oneof [+ C.SetAdd <$> arbitrary, C.SetRemove <$> arbitrary+ ]++instance Arbitrary C.FlagOp where+ arbitrary = C.FlagSet <$> arbitrary++instance Arbitrary C.Set where+ arbitrary = C.Set . Set.fromList <$> arbitrary++instance Arbitrary ByteString where+ arbitrary = elements [ "foo", "bar", "baz" ]++instance Arbitrary C.MapOp where+ arbitrary = C.MapUpdate <$> arbitrary <*> arbitrary++instance Arbitrary C.MapPath where+ arbitrary = (\a b -> C.MapPath (a :| b)) <$> arbitrary <*> arbitrary++instance Arbitrary C.MapField where+ arbitrary = C.MapField <$> arbitrary <*> arbitrary++instance Arbitrary C.MapEntryTag where+ arbitrary = elements [ C.MapCounterTag ]++instance Arbitrary C.MapValueOp where+ arbitrary = oneof [ C.MapCounterOp <$> arbitrary,+ C.MapSetOp <$> arbitrary,+ C.MapFlagOp <$> arbitrary ]++-- TODO Fix this+instance Arbitrary C.Map++-- | Abstract machine.+-- Yields a value on 'Get', modifies state on 'Update'.+machine :: (MonadWriter [Maybe C.DataType] m,+ MonadState s m,+ Applicative m,+ Action t op)+ => Proxy t -> [Op t op]+ -> (Op t op -> s -> m (Either (Maybe C.DataType) s)) -> m ()++machine _ [] _ = pure ()++machine p (a@Get{} : as) onAct = do+ v <- get+ Left r <- onAct a v+ tell [r]+ machine p as onAct++machine p (a@Update{} : as) onAct = do+ v <- get+ Right r <- onAct a v+ put r+ machine p as onAct++++-- | Riak version of the 'machine'.+-- State is 'B.Connection', get/update are IO-requests to riak.+riak :: (MonadWriter [Maybe C.DataType] m,+ MonadState B.Connection m,+ Applicative m, MonadIO m,+ Action t op)+ => Proxy t -> [Op t op] -> m ()++riak p ops = machine p ops onAct+ where onAct (Get (Bucket b) (Key k)) c+ = liftIO $ Left <$> C.get c bt b k+ onAct (Update (Bucket b) (Key k) op) c+ = do liftIO $ C.sendModify c bt b k [op]+ pure $ Right c+ bt = bucketType p++++-- | Haskell emulation version of the 'machine'.+-- State is 'RiakState', get/update try to match riak's behaviour.+pure_ :: (MonadWriter [Maybe C.DataType] m,+ MonadState RiakState m,+ Applicative m,+ Action t op)+ => Proxy t -> [Op t op] -> m ()++pure_ p ops = machine p ops onAct+ where+ onAct (Get b k) v = pure . Left $ Map.lookup (point b k) v+ onAct (Update b k op) v = pure . Right $ Map.alter (update op) (point b k) v+ point b k = Point (BucketType (bucketType p)) b k++++update :: forall a op. (Action a op) =>+ op -> Maybe C.DataType -> Maybe C.DataType+update op dt+ -- | Dear diary, this is getting out of hand. It'd be far easier to+ -- not bother and assume Nothing ≡ Just mempty in test conditions.+ --+ -- …but let's continue to bother and gain more safety.+ | Just d <- dt, targetThere dt op+ = operate d+ | updateCreates (Proxy :: Proxy a) dt op+ -- it's sometimes ok to update even a non-set value in riak's mind+ = operate $ maybe surrogate id dt+ | otherwise+ = dt+ where fromDT' :: C.DataType -> a+ fromDT' = fromDT+ toDT' :: a -> C.DataType+ toDT' = toDT+ surrogate = toDT' def+ operate = Just . toDT . C.modify op . fromDT'++++doRiak :: Action a op =>+ Proxy a -> [Op a op] -> IO [Maybe C.DataType]+doRiak p ops = withSomeConnection $ \conn -> do+ --print ops+ (_,_,r) <- runRWST (riak p ops) () conn+ pure r++doPure :: (Action a op, Show op) =>+ RiakState -> Proxy a -> [Op a op] -> PropertyM IO [Maybe C.DataType]+doPure stat p ops = do (_,_,r) <- runRWST (pure_ p ops) () stat+ pure r+++++prop :: (Show op, Action a op) =>+ Proxy a -> [Op a op] -> Property+prop p ops = monadicIO $ do+ stat <- run $ observeRiak p+ r1 <- doPure stat p ops+ r2 <- run $ doRiak p ops+ run . when (r1/=r2) $ print (r1,r2)+ assert $ r1 == r2+++prop_counters :: [Op C.Counter C.CounterOp] -> Property+prop_counters = prop (Proxy :: Proxy C.Counter)++prop_sets :: [Op C.Set C.SetOp] -> Property+prop_sets = prop (Proxy :: Proxy C.Set)++prop_maps :: [Op C.Map C.MapOp] -> Property+prop_maps = prop (Proxy :: Proxy C.Map)++++tests :: TestTree+tests = testGroup "CRDT quickCheck" [+ testProperty "counters" prop_counters,+ testProperty "sets" prop_sets,+ testProperty "maps" prop_maps+ ]
+ tests/Common.hs view
@@ -0,0 +1,17 @@+module Common (withSomeConnection) where++import qualified Network.Riak.Basic as B+import Network.Riak.Connection.Pool (Pool, create, withConnection)+import Network.Riak.Connection (defaultClient)+import System.IO.Unsafe (unsafePerformIO)+++pool :: Pool+{-# NOINLINE pool #-}+pool = unsafePerformIO $+ create defaultClient 1 1 1++-- | run action in some riak connection+withSomeConnection :: (B.Connection -> IO a) -> IO a+withSomeConnection = withConnection pool+
tests/Properties.hs view
@@ -9,39 +9,56 @@ import Control.Applicative ((<$>)) #endif import qualified Data.ByteString.Lazy as L-import Data.IORef (IORef, modifyIORef, newIORef)+import Data.Maybe import qualified Network.Riak.Basic as B-import Network.Riak.Connection (defaultClient)-import Network.Riak.Connection.Pool (Pool, create, withConnection) import Network.Riak.Content (binary)-import Network.Riak.Types (Bucket, Key, Quorum (..))-import System.IO.Unsafe (unsafePerformIO)+import Network.Riak.Types as Riak import Test.QuickCheck.Monadic (assert, monadicIO, run) import Test.Tasty import Test.Tasty.QuickCheck+import Common instance Arbitrary L.ByteString where arbitrary = L.pack `fmap` arbitrary -cruft :: IORef [(Bucket, Key)]-{-# NOINLINE cruft #-}-cruft = unsafePerformIO $ newIORef []+newtype QCBucket = QCBucket Riak.Bucket deriving Show -pool :: Pool-{-# NOINLINE pool #-}-pool = unsafePerformIO $- create defaultClient 1 1 1+instance Arbitrary QCBucket where+ arbitrary = QCBucket <$> arbitrary `suchThat` (not . L.null) -t_put_get :: Bucket -> Key -> L.ByteString -> Property-t_put_get b k v =- notempty b && notempty k ==> monadicIO $ assert . uncurry (==) =<< run act+newtype QCKey = QCKey Riak.Key deriving Show++instance Arbitrary QCKey where+ arbitrary = QCKey <$> arbitrary `suchThat` (not . L.null)+++t_put_get :: QCBucket -> QCKey -> L.ByteString -> Property+t_put_get (QCBucket b) (QCKey k) v =+ monadicIO $ assert . uncurry (==) =<< run act where- act = withConnection pool $ \c -> do- modifyIORef cruft ((b,k):)+ act = withSomeConnection $ \c -> do p <- Just <$> B.put c b k Nothing (binary v) Default Default r <- B.get c b k Default return (p,r)- notempty = not . L.null ++put_delete_get :: QCBucket -> QCKey -> L.ByteString -> Property+put_delete_get (QCBucket b) (QCKey k) v+ = monadicIO $ do+ r <- run act+ assert $ isNothing r+ where+ act = withSomeConnection $ \c -> do+ _ <- B.put c b k Nothing (binary v) Default Default+ B.delete c b k Default+ B.get c b k Default++ tests :: [TestTree]-tests = [ testProperty "t_put_get" t_put_get ]+tests = [+ testProperty "t_put_get" t_put_get,+ testProperty "put_delete_get" put_delete_get+ ]+++
tests/Test.hs view
@@ -3,43 +3,61 @@ module Main where -import Control.Exception (finally)-import Control.Monad (forM_)-import Data.IORef+import Control.Monad import qualified Data.Map as M+import qualified Data.Set as S+import Data.List.NonEmpty (NonEmpty(..))+import Data.Semigroup import Data.Text (Text)+import Control.Concurrent (threadDelay) import qualified Network.Riak as Riak import qualified Network.Riak.Basic as B+import qualified Network.Riak.CRDT as C+import qualified Network.Riak.CRDT.Riak as C+import qualified Network.Riak.Search as S import qualified Network.Riak.Cluster as Riak-import Network.Riak.Connection.Pool (withConnection) import qualified Network.Riak.JSON as J import Network.Riak.Resolvable (ResolvableMonoid (..))-import Network.Riak.Types+import Network.Riak.Types hiding (key) import qualified Properties+import qualified CRDTProperties as CRDT import Test.Tasty import Test.Tasty.HUnit main :: IO ()-main = defaultMain tests `finally` cleanup- where- cleanup = withConnection Properties.pool $ \c -> do- bks <- readIORef Properties.cruft- forM_ bks $ \(b,k) -> B.delete c b k Default+main = defaultMain tests -tests :: TestTree-tests = testGroup "Tests" [properties, integrationalTests] +tests :: TestTree+tests = testGroup "Tests" [properties,+ integrationalTests,+ ping'o'death,+ crdts,+ searches+ ] properties :: TestTree-properties = testGroup "Properties" Properties.tests+properties = testGroup "simple properties" Properties.tests integrationalTests :: TestTree-integrationalTests = testGroup "Integrational tests"+integrationalTests = testGroup "integrational tests" [ testClusterSimple #ifdef TEST2I , testIndexedPutGet #endif ] +crdts :: TestTree+crdts = testGroup "CRDT" [+ testGroup "simple" [counter, set, map_],+ CRDT.tests+ ]++searches :: TestTree+searches = testGroup "Search" [+ search,+ getIndex+ ]+ testClusterSimple :: TestTree testClusterSimple = testCase "testClusterSimple" $ do rc <- Riak.connectToCluster [Riak.defaultClient]@@ -57,3 +75,101 @@ Default Default Riak.getByIndex c b (IndexQueryExactInt "someindex" 135) assertEqual "" ["test"] keys++ping'o'death :: TestTree+ping'o'death = testCase "ping'o'death" $ replicateM_ 23 ping+ where ping = do+ c <- Riak.connect Riak.defaultClient+ replicateM_ 1024 $ Riak.ping c+++counter :: TestTree+counter = testCase "increment" $ do+ conn <- Riak.connect Riak.defaultClient+ Just (C.DTCounter (C.Counter a)) <- act conn+ Just (C.DTCounter (C.Counter b)) <- act conn+ assertEqual "inc by 1" 1 (b-a)+ where+ act c = do C.counterSendUpdate c "counters" "xxx" "yyy" [C.CounterInc 1]+ C.get c "counters" "xxx" "yyy"++set :: TestTree+set = testCase "set add" $ do+ conn <- Riak.connect Riak.defaultClient+ C.setSendUpdate conn btype buck key [C.SetRemove val]+ C.setSendUpdate conn btype buck key [C.SetAdd val]+ Just (C.DTSet (C.Set r)) <- C.get conn btype buck key+ assertBool "-foo +foo => contains foo" $ val `S.member` r+ where+ (btype,buck,key,val) = ("sets","xxx","yyy","foo")++map_ :: TestTree+map_ = testCase "map update" $ do+ conn <- Riak.connect Riak.defaultClient+ Just (C.DTMap a) <- act conn -- do smth (increment), get+ Just (C.DTMap b) <- act conn -- increment, get+ assertEqual "map update" (C.modify mapOp a) b -- modify's behaviour should match+ assertEqual "mapUpdate sugar" mapOp' mapOp++ -- remove top-level field (X)+ C.mapSendUpdate conn btype buck key [C.MapRemove fieldX]+ Just (C.DTMap c) <- act conn+ assertEqual "update after delete" (Just updateCreates) $ C.xlookup "X" C.MapMapTag c++ -- remove nested field (X/Y)+ C.mapSendUpdate conn btype buck key [C.MapUpdate (C.MapPath $ "X" :| [])+ (C.MapMapOp (C.MapRemove fieldY))]+ Just (C.DTMap d) <- C.get conn btype buck key+ assertEqual "update after nested delete 1" Nothing+ $ C.xlookup ("X" C.-/ "Y") C.MapMapTag d+ assertEqual "update after nested delete 2" (Just (C.MapMap (C.Map mempty)))+ $ C.xlookup "X" C.MapMapTag d+ where+ act c = do C.mapSendUpdate c btype buck key [mapOp]+ C.get c btype buck key ++ btype = "maps"+ fieldX = C.MapField C.MapMapTag "X"+ fieldY = C.MapField C.MapMapTag "Y"+ (buck,key) = ("xxx","yyy")++ updateCreates = C.MapMap (C.Map (M.fromList+ [(C.MapField C.MapMapTag "Y",+ C.MapMap (C.Map (M.fromList+ [(C.MapField C.MapCounterTag "Z",+ C.MapCounter (C.Counter 1))])))]))++ mapOp = "X" C.-/ "Y" C.-/ "Z" `C.mapUpdate` C.CounterInc 1++ mapOp' = C.MapUpdate (C.MapPath ("X" :| "Y" : "Z" : []))+ (C.MapCounterOp (C.CounterInc 1))+++search :: TestTree+search = testCase "basic searchRaw" $ do+ conn <- Riak.connect Riak.defaultClient+ C.sendModify conn btype buck key [C.SetRemove kw]+ delay+ a <- query conn ("set:" <> kw)+ assertEqual "should not found non-existing" [] a+ C.sendModify conn btype buck key [C.SetAdd kw]+ delay+ b <- query conn ("set:" <> kw)+ assertBool "searches specific" $ not (null b)+ c <- query conn ("set:*")+ assertBool "searches *" $ not (null c)+ where+ query conn q = S.searchRaw conn q "set-ix"+ (btype,buck,key) = ("sets","xxx","yyy")+ kw = "haskell"+ delay = threadDelay (1*5000*1000) -- http://docs.basho.com/riak/2.1.3/dev/using/search/#Indexing-Values+++getIndex :: TestTree+getIndex = testCase "getIndex" $ do+ conn <- Riak.connect Riak.defaultClient+ all <- S.getIndex conn Nothing+ one <- S.getIndex conn (Just "set-ix")+ assertBool "all indeces" $ not (null all)+ assertEqual "set index" 1 (length one)+