FakeRiak.RiakPBC (fake_riak v0.3.0)

Copy Markdown View Source

The Protocol Buffers (PBC) side of the fake Riak endpoint.

Every message is framed as <<length::32-big, msg_code::8, payload>> where length counts the code byte plus the protobuf payload. Supported messages:

CodeRequestReply
1RpbPingReq2 RpbPingResp
3RpbGetClientIdReq4 RpbGetClientIdResp (static id)
5RpbSetClientIdReq6 RpbSetClientIdResp (ignored)
7RpbGetServerInfoReq8 RpbGetServerInfoResp
9RpbGetReq10 RpbGetResp (empty when absent)
11RpbPutReq12 RpbPutResp
13RpbDelReq14 RpbDelResp (always succeeds)
15RpbListBucketsReq16 RpbListBucketsResp (single frame)
17RpbListKeysReq18 RpbListKeysResp (single frame)
19RpbGetBucketReq20 RpbGetBucketResp (static props)
21RpbSetBucketReq22 RpbSetBucketResp (ignored)
25RpbIndexReq26 RpbIndexResp (single frame)
29RpbResetBucketReq30 RpbResetBucketResp (ignored)
31RpbGetBucketTypeReq20 RpbGetBucketResp (static props)
32RpbSetBucketTypeReq22 RpbSetBucketResp (ignored)
50RpbCounterUpdateReq51 RpbCounterUpdateResp
52RpbCounterGetReq53 RpbCounterGetResp
80DtFetchReq81 DtFetchResp
82DtUpdateReq83 DtUpdateResp
253RpbAuthReq254 RpbAuthResp (always succeeds)
255RpbStartTls255, then a TLS handshake

Any other code gets an RpbErrorResp (0) and the connection stays open. There is no real security model: RpbAuthReq accepts any username and password (or none) and always replies with success, so clients configured for auth against a real Riak cluster still work against the fake one.

Bucket properties are not modelled: RpbSetBucketReq and friends are accepted and ignored, and RpbGetBucketReq reports a fixed set of props that happen to describe how the fake actually behaves — a default n_val with allow_mult true, since vclock-less puts accumulate as siblings. The client id is cosmetic too: the vclock is a static opaque token that never depends on an actor id, so RpbSetClientIdReq is ignored and RpbGetClientIdReq returns a fixed id. Objects are stored as their raw RpbContent.value in the same bucketed FakeRiak.SimpleStore space the HTTP API uses, so the two APIs see each other's writes. Each key holds a list of siblings: an RpbPutReq without a vclock (field 3) is a blind write that appends a new sibling, while one that carries a vclock resolves the key down to the single value it sends. An RpbGetResp returns every sibling as a repeated RpbContent in field 1. Vector clocks are a static opaque token: clients only ever echo them back, so it is their presence on a put — not their content — that signals resolution.

Legacy counters (RpbCounterUpdateReq/RpbCounterGetReq) are Riak's pre-CRDT counters. They live in a separate signed-integer keyspace in SimpleStore, not the object space above: an update adds a signed sint64 amount to the current value (0 if unset), and a get of a counter that was never updated yields an empty response, the way real Riak reports an unset counter.

Secondary indexes (2i) are PBC-only here — the HTTP side does not implement them. Indexes ride along on the object: an RpbPutReq carries them as field 10 of its RpbContent (a repeated RpbPair of index name and term), and the put path stashes them per (bucket, key) in SimpleStore, following the put's own merge mode (a vclock-resolving put overwrites the entries, a blind sibling put unions them). Deleting the key drops its entries. RpbIndexReq answers eq and range queries as a single done-framed RpbIndexResp (the degenerate stream clients accept); index names ending in _int compare numerically, everything else lexicographically. return_terms adds the matching {term, key} pairs as results; the paging options (max_results/continuation/term_regex/…) are not honored.

CRDT data types (DtFetchReq/DtUpdateReq) cover counter, set and gset; map and hll are out of scope and their update ops return an error. A real Riak learns a key's datatype from the bucket-type config, which the fake does not have, so an update infers it from the op present (counter_op/set_op/ gset_op) and stores the tag so a fetch reports the right type. Data is keyed by (bucket-type, bucket, key) in a separate SimpleStore keyspace. The context is a static opaque token like the vclock — clients only echo it back — so set removes are best-effort (they apply directly, with no real causal context), and a gset only grows. A fetch of a key that was never updated is a "not found" reported as the default COUNTER type with no value.

This module is not wired to a listener itself: FakeRiak.Riak detects PBC framing on the first byte of a connection and delegates here, passing its own dispatch state through untouched (requests are independent, so no state of our own is needed).

Summary

Functions

Wrap a message code and protobuf payload in a PBC frame.

Functions

frame(code, payload)

@spec frame(0..255, iodata()) :: binary()

Wrap a message code and protobuf payload in a PBC frame.