From e5aadecc345614a8cf89ef9615c87f960f633656 Mon Sep 17 00:00:00 2001 From: Victor Grishchenko Date: Mon, 6 May 2024 10:08:31 +0700 Subject: [PATCH] Enveloping commentary --- orm.go | 1 + rdx/ELM_test.go | 16 ++++++++++++++++ rdx/README.md | 31 ++++++++++++++++++++++++++++++- 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/orm.go b/orm.go index dc45db0..c4e9e55 100644 --- a/orm.go +++ b/orm.go @@ -143,6 +143,7 @@ func (orm *ORM) Close() error { return ErrClosed } orm.objects.Clear() + orm.ids.Clear() orm.Host = nil _ = orm.Snap.Close() orm.Snap = nil diff --git a/rdx/ELM_test.go b/rdx/ELM_test.go index 201b875..340e66a 100644 --- a/rdx/ELM_test.go +++ b/rdx/ELM_test.go @@ -7,6 +7,22 @@ import ( "github.com/stretchr/testify/assert" ) +func TestMap(t *testing.T) { + var correct = []byte{ + 0x6D, 0x15, + 0x36, 0x03, 0x00, 0xaf, 0x00, 0x0b, 0x0b, + 0x73, 0x04, 0x30, 0x4b, 0x65, 0x79, + 0x73, 0x06, 0x30, 0x56, 0x61, 0x6c, 0x75, 0x65, + } + id := IDFromString("b0b-af0-3") + env2 := protocol.Record('M', + protocol.TinyRecord('I', id.ZipBytes()), + protocol.Record('S', protocol.TinyRecord('T', ZipIntUint64Pair(0, 0)), []byte("Key")), + protocol.Record('S', protocol.TinyRecord('T', ZipIntUint64Pair(0, 0)), []byte("Value")), + ) + assert.Equal(t, correct, env2) +} + func TestEmerge(t *testing.T) { tlv1 := Eparse("{1, 2, \"four\"}") assert.Equal(t, "{1,2,\"four\"}", Estring(tlv1)) diff --git a/rdx/README.md b/rdx/README.md index 230f7a0..acf9742 100644 --- a/rdx/README.md +++ b/rdx/README.md @@ -156,7 +156,7 @@ to produce `I{1,3}1 T{-4,4} I{2,3}2 I{3,3}3` or `[2,3]`. The string value for an array is like `[1,2,3]` -### `V` +### `V` Version vector [Version vector][v] is a way to track dataset versions in a causally ordered system. It is a vector of `seq` numbers, where @@ -245,6 +245,35 @@ which has a separate bit-level [LEB128][b] coding for ints). id64 and logical timestamps get packed as pairs of uint64s. All zip codings are little-endian. +## Enveloping + +RDX values can be bare, enveloped or double-enveloped. We use +bare values when we already know what field of what object we +are dealing with and what RDT it belongs to. That might be the +case when we read a value from a key-value storage where the key +contains object id, field and RDT. In such a case, a bare +Integer is like `{3,2}1` or `32 03 02 02`. + +Within a network packet, that integer may need to be +single-enveloped: `I({3,2}1)` or `69 04 32 03 02 02` assuming +the other metadata is known from the context. + +A bare `ELM` or `NZ` value would only contain a sequence of +single-enveloped `FIRST` values. To make that single-enveloped +we only prepend a TLV header. + +In case we also have to convey the rest of the metadata, namely +the object id and the field, we have to use the double-enveloped +form. For a simple `map[string]string{"Key":"Value"}` that +looks like: `M({b0b-af0-3} S({0,0}"Key") S({0,0}"Value"))` or +`6D 15 36 03 00 af 00 0b 0b 73 04 30 4b 65 79 73 06 30 56 61 6c 75 65`. +For `FIRST` values, there is no need to use two nested TLV +records, so a double-enveloped Integer looks like: +`I({b0b-af0-7}{3,2}1)` + +Object/fields ids are serialized as tiny `ZipUint64Pair`s. +Revisions are serialized as tiny `ZipIntUint64Pair`s. + [x]: https://en.wikipedia.org/wiki/Causal_consistency [v]: https://en.wikipedia.org/wiki/Version_vector [r]: https://www.educative.io/answers/how-are-vector-clocks-used-in-dynamo