From bd797b30ae54c9d407880dc9340cbe5a2d0bd186 Mon Sep 17 00:00:00 2001 From: Victor Grishchenko Date: Sun, 14 Apr 2024 16:10:58 +0700 Subject: [PATCH] clarity edits --- README.md | 30 +++++++++++++++++------------- rdx/ELM.go | 8 ++++---- rdx/rdx.go | 12 ++++++------ rdx/rdx.ragel.go | 18 +++++++++--------- repl/commands.go | 14 +++++++------- 5 files changed, 43 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index a3cf8b9..105bff3 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![Build Status](https://github.com/drpcorg/chotki/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/drpcorg/chotki/actions/workflows/test.yml) + Chotki is a syncable store with really fast counters. Internally, it is [pebble db][p] running CRDT natively, using the [Replicated Data Interchange][r] format (RDX). Chotki is @@ -61,9 +62,9 @@ is very close to JSON, e.g. ```` Each command returns an ID and/or an error. -Text RDX is different from JSON in several aspects: it has -explicit ID type, arbitrary literals apart from `true`, `false` -and `null`, set collections and some other minor differences. +Text RDX is different from JSON in several aspects: it has the +ID type, arbitrary literals apart from `true`, `false` and +`null`, also set collections and some other minor differences. On the human readability side, it is pretty much the same thing. ### HTTP @@ -79,12 +80,14 @@ serialization/parsing, etc. ## Replicas -The N1 superpower of Chotki is syncing. Replicas may work +The main superpower of Chotki is syncing. Replicas may work offline, reconnect and resync, or they may sync continuously in real time. Chotki so far only supports a spanning-tree overlay network topology. Each replica has a 20-bit "name" (aka *source*); a replica can only connect to replicas with lesser src number to prevent loops. +E.g. `a1ece` can connect to `b0b`, but not the other way around +(replica ids are given in hex). Implementations of client replicas working on mobile devices or in a browser are planned. @@ -93,8 +96,8 @@ in a browser are planned. Chotki is based on pebble db, which is an LSM database. A superpower of LSM is "blind writes", i.e. writes with no -preceing read necessary. On a Lenovo Yoga laptop, a Chotki -replica can do about 1mln blind increments to a counter in about +preceding read necessary. On a Lenovo Yoga laptop, a Chotki +replica can do about 1mln blind increments of a counter in about 3 seconds, be it connected to other replicas or not: ```` ◌ sinc {fid:b0b-6-2,count:10000000,ms:0} @@ -124,7 +127,7 @@ One may check the [first 2017 RON/RDX talk][c] manifesting the project's goals. In that regard, we may compare RON/RDX to [Automerge][a], which is a project of exactly the same age. Both projects started with a columnar-like coding of operations, -which Automerge is using to this day, while RDX followed +which Automerge is using to this day, while RDX followed the Einstein's maxim: "Everything should be made as simple as possible, but not simpler". After spending quite some time to [cram][s] columnar-encoded CRDT into exising databases, RDX was @@ -137,17 +140,18 @@ We can also compare Chotki to a number of JavaScript-centric CRDT databases, such as [RxDB][x] or [SyncedStore][z]. Historically RON/RDX also has it roots in the JavaScript world. [Swarm.js][j] was likely the first CRDT sync lib in history -(2013-2018); although it was distilled from the earlier Citrea -project (2011-2012). Still, Chotki/RDX has an objective of -creating a production-ready scalable CRDT store, which -JavaScript does not really allow. Still, we will be extremely -happy if some of the JavaScript libs would support RDX -as a unifying format. (Ping us any time!) +(2013-2018); although it was distilled from the earlier +[Citrea][t] project (2011-2012). Still, Chotki/RDX has an +objective of creating a production-ready scalable CRDT store, +which JavaScript does not really allow. Still, we will be +extremely happy if some of the JavaScript libs would consider +supporting RDX as a unifying format. (Ping us any time!) [j]: https://github.com/gritzko/swarm [a]: https://automerge.org/ [c]: https://www.youtube.com/watch?v=0Xx9kkTMi10 [s]: https://www.youtube.com/live/M8RRZakZgiI?si=yQVT0Le7FlnpfWXw&t=32187 +[t]: https://github.com/gritzko/citrea-model [x]: https://github.com/pubkey/rxdb [z]: https://syncedstore.org/docs/ diff --git a/rdx/ELM.go b/rdx/ELM.go index 8e50758..ea82e34 100644 --- a/rdx/ELM.go +++ b/rdx/ELM.go @@ -113,7 +113,7 @@ func appendFirstTlvString(tlv []byte, lit byte, bare []byte) []byte { // parse a text form into a TLV value func Eparse(txt string) (tlv []byte) { rdx, err := ParseRDX([]byte(txt)) - if err != nil || rdx == nil || rdx.RdxType != ESet { + if err != nil || rdx == nil || rdx.RdxType != Eulerian { return nil } for i := 0; i < len(rdx.Nested); i++ { @@ -222,7 +222,7 @@ func Mstring(tlv []byte) (txt string) { // parse a text form into a TLV value func Mparse(txt string) (tlv []byte) { rdx, err := ParseRDX([]byte(txt)) - if err != nil || rdx == nil || rdx.RdxType != Map { + if err != nil || rdx == nil || rdx.RdxType != Mapping { return nil } for i := 0; i < len(rdx.Nested); i++ { @@ -338,7 +338,7 @@ func Lparse(txt string) (tlv []byte) { bm, tlv = toytlv.OpenHeader(tlv, 'B') tlv = append(tlv, '0') rdx, err := ParseRDX([]byte(txt)) - if err != nil || rdx == nil || rdx.RdxType != LArray { + if err != nil || rdx == nil || rdx.RdxType != Linear { return nil } for i := 0; i < len(rdx.Nested); i++ { @@ -473,7 +473,7 @@ func (a *LIterator) Merge(bb SortedIterator) int { } func Mrdx2tlv(a *RDX) (tlv []byte) { - if a == nil || a.RdxType != Map { + if a == nil || a.RdxType != Mapping { return nil } for i := 0; i+1 < len(a.Nested); i += 2 { diff --git a/rdx/rdx.go b/rdx/rdx.go index 79a382d..08c9538 100644 --- a/rdx/rdx.go +++ b/rdx/rdx.go @@ -16,9 +16,9 @@ const ( NInc = byte('n') ZCounter = byte('Z') ZInc = byte('z') - ESet = byte('E') - LArray = byte('L') - Map = byte('M') + Eulerian = byte('E') + Linear = byte('L') + Mapping = byte('M') ) type RDX struct { @@ -80,7 +80,7 @@ func (rdx *RDX) Feed() (recs toyqueue.Records, err error) { recs = append(recs, rdx.Text) case Natural, NInc, ZCounter, ZInc: recs = append(recs, rdx.Text) - case Map: + case Mapping: recs = append(recs, RdxSep[RdxOOpen:RdxOOpen+1]) for i := 0; i+1 < len(rdx.Nested); i += 2 { key, _ := rdx.Nested[i].Feed() @@ -93,7 +93,7 @@ func (rdx *RDX) Feed() (recs toyqueue.Records, err error) { } } recs = append(recs, RdxSep[RdxOClose:RdxOClose+1]) - case ESet: + case Eulerian: recs = append(recs, RdxSep[RdxOOpen:RdxOOpen+1]) for i := 0; i < len(rdx.Nested); i++ { val, _ := rdx.Nested[i].Feed() @@ -103,7 +103,7 @@ func (rdx *RDX) Feed() (recs toyqueue.Records, err error) { } } recs = append(recs, RdxSep[RdxOClose:RdxOClose+1]) - case LArray: + case Linear: recs = append(recs, RdxSep[RdxAOpen:RdxAOpen+1]) for i := 0; i < len(rdx.Nested); i++ { val, _ := rdx.Nested[i].Feed() diff --git a/rdx/rdx.ragel.go b/rdx/rdx.ragel.go index bd2f281..e250acd 100644 --- a/rdx/rdx.ragel.go +++ b/rdx/rdx.ragel.go @@ -190,7 +190,7 @@ func ParseRDX(data []byte) (rdx *RDX, err error) { n := rdx.Nested n = append(n, RDX{Parent: rdx}) rdx.Nested = n - rdx.RdxType = Map + rdx.RdxType = Mapping rdx = &n[len(n)-1] nest++ } @@ -208,7 +208,7 @@ func ParseRDX(data []byte) (rdx *RDX, err error) { } nest-- rdx = rdx.Parent - if rdx.RdxType != ESet && rdx.RdxType != Map { + if rdx.RdxType != Eulerian && rdx.RdxType != Mapping { cs = _RDX_error { p += 1 @@ -218,9 +218,9 @@ func ParseRDX(data []byte) (rdx *RDX, err error) { } if len(rdx.Nested) == 1 { - rdx.RdxType = ESet + rdx.RdxType = Eulerian } - if rdx.RdxType == Map { + if rdx.RdxType == Mapping { if (len(rdx.Nested) & 1) == 1 { cs = _RDX_error { @@ -238,7 +238,7 @@ func ParseRDX(data []byte) (rdx *RDX, err error) { n := rdx.Nested n = append(n, RDX{Parent: rdx}) rdx.Nested = n - rdx.RdxType = LArray + rdx.RdxType = Linear rdx = &n[len(n)-1] nest++ } @@ -256,7 +256,7 @@ func ParseRDX(data []byte) (rdx *RDX, err error) { } nest-- rdx = rdx.Parent - if rdx.RdxType != LArray { + if rdx.RdxType != Linear { cs = _RDX_error { p += 1 @@ -279,9 +279,9 @@ func ParseRDX(data []byte) (rdx *RDX, err error) { } n := rdx.Parent.Nested - if rdx.Parent.RdxType == Map { + if rdx.Parent.RdxType == Mapping { if len(n) == 1 { - rdx.Parent.RdxType = ESet + rdx.Parent.RdxType = Eulerian } else if (len(n) & 1) == 1 { cs = _RDX_error { @@ -309,7 +309,7 @@ func ParseRDX(data []byte) (rdx *RDX, err error) { } n := rdx.Parent.Nested - if rdx.Parent.RdxType == Map { + if rdx.Parent.RdxType == Mapping { if (len(n) & 1) == 0 { cs = _RDX_error { diff --git a/repl/commands.go b/repl/commands.go index c9d9eec..28a0cdf 100644 --- a/repl/commands.go +++ b/repl/commands.go @@ -23,7 +23,7 @@ func (repl *REPL) CommandCreate(arg *rdx.RDX) (id rdx.ID, err error) { name := "Unnamed replica" if arg.RdxType == rdx.Reference { src = rdx.IDFromText(arg.Text) - } else if arg.RdxType == rdx.Map { + } else if arg.RdxType == rdx.Mapping { for i := 0; i+1 < len(arg.Nested); i += 2 { key := arg.Nested[i] val := arg.Nested[i+1] @@ -112,7 +112,7 @@ var HelpClass = errors.New( func (repl *REPL) CommandClass(arg *rdx.RDX) (id rdx.ID, err error) { id = rdx.BadId err = HelpClass - if arg == nil || arg.RdxType != rdx.Map || len(arg.Nested) < 2 { + if arg == nil || arg.RdxType != rdx.Mapping || len(arg.Nested) < 2 { return } fields := arg.Nested @@ -154,9 +154,9 @@ func (repl *REPL) CommandNew(arg *rdx.RDX) (id rdx.ID, err error) { tlvs := toyqueue.Records{} if arg == nil { return - } else if arg.RdxType == rdx.LArray { + } else if arg.RdxType == rdx.Linear { return - } else if arg.RdxType == rdx.Map { + } else if arg.RdxType == rdx.Mapping { pairs := arg.Nested if len(pairs) >= 2 && pairs[0].String() == "_ref" { tid = rdx.IDFromText(pairs[1].Text) @@ -207,7 +207,7 @@ var HelpEdit = errors.New( func (repl *REPL) CommandEdit(arg *rdx.RDX) (id rdx.ID, err error) { id = rdx.BadId err = HelpEdit - if arg == nil || arg.RdxType != rdx.Map || len(arg.Nested) < 2 { + if arg == nil || arg.RdxType != rdx.Mapping || len(arg.Nested) < 2 { return } if arg.Nested[0].String() == "_id" { @@ -458,7 +458,7 @@ func (repl *REPL) CommandTinc(arg *rdx.RDX) (id rdx.ID, err error) { return } else if arg.RdxType == rdx.Reference { id = rdx.IDFromText(arg.Text) - } else if arg.RdxType == rdx.Map { + } else if arg.RdxType == rdx.Mapping { for i := 0; i+1 < len(arg.Nested); i += 2 { key := &arg.Nested[i] val := &arg.Nested[i+1] @@ -517,7 +517,7 @@ func (repl *REPL) CommandSinc(arg *rdx.RDX) (id rdx.ID, err error) { return } else if arg.RdxType == rdx.Reference { id = rdx.IDFromText(arg.Text) - } else if arg.RdxType == rdx.Map { + } else if arg.RdxType == rdx.Mapping { for i := 0; i+1 < len(arg.Nested); i += 2 { key := &arg.Nested[i] val := &arg.Nested[i+1]