chotki

package module
v0.0.0-...-6a3944b Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 5, 2025 License: MIT Imports: 28 Imported by: 0

README

◌ Chotki: fast syncable store

godoc MIT License Build Status

Chotki is a syncable store with really fast counters. Internally, it is pebble db running CRDT natively, using the Replicated Data Interchange format (RDX). Chotki is sync-centric and causally consistent. That means, Chotki replicas can sync master-master, either incrementally in real-time or stay offline to diff-sync periodically. Chotki API is REST/object based, no SQL yet.

Use

Chotki's data model has three key entities:

  1. a class is a list of named fields,
  2. an object is an instance of a class,
  3. a field is a value of a Replicated Data Type (RDT).

Available RDTs include basic "last-write-wins" FIRST types:

  1. Float,
  2. Integer,
  3. Reference,
  4. String and
  5. Term.

There are also collections/ELM types:

  1. Eulerian (set) is an unordered collection of FIRST values,
  2. Linear (array) is an ordered collection of FIRST values, and
  3. Mapping is a collection of key-value pairs (both FIRST).

There are also counters/NZ types:

  1. N are "natural" increment-only uint64 counters and
  2. Z are signed int64 increment/decrement counters.

Objects can Reference each other thus forming object graphs. As an example, an object of class Team has a field Players which is a E-set of references to objects of class Player. Each object of class Player has an Integer field Number, a String field Name, and a counter NetWorth.

Chotki does not support any filter queries yet (of select ... where type). It can fetch an object by its ID or recursively by references (a reference is an ID).

Replicas

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 number (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 numbers are given in hex).

Implementations of client replicas working on mobile devices or in a browser are planned.

Installation

go get -u github.com/drpcorg/chotki

Inner workings

Internally, Chotki is pebble db using RDX merge operators. See the RDX doc for further details on its serialization format (type-length-value) and a very predictable choice of CRDTs.

Comparison to other projects

Overall, RDX is the final version of RON (Replicated Object Notation), a research project that lasted from 2017 till 2022. One may check the first 2017 RON/RDX talk manifesting the project's goals. In that regard, we may compare RON/RDX to Automerge, 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 the Einstein's maxim: "Everything should be made as simple as possible, but not simpler". After spending quite some time to cram columnar-encoded CRDT into exising databases, RDX was greatly simplified and now all the RDX CRDT logic fits into a merge operator. That greatly improved the performance. Effectively, that simplicity allows to use a commodity LSM storage engine to natively store arbitrary CRDT.

We can also compare Chotki to a number of JavaScript-centric CRDT databases, such as RxDB, TinyBase or SyncedStore. Historically RON/RDX also has it roots in the JavaScript world. Swarm.js was likely the first CRDT sync lib in history (2013-2018); although it was distilled from the earlier Citrea project (2011-2012). 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!)

The original Chotki project summary

The project is a collaboration of dRPC.org and Victor "gritzko" Grishchenko. RDX types are based on the RON/RDX research project. The following is the original project summary:

The mission of the system is to keep and update real-time statistics, such as quotas, counters, billing and suchlike. Update propagation time is expected to be close to the theoretic minimum: the one-way delay. We expect to be able to process data at bulk transfer speeds (i.e. as much as we can move by the network we can process in real-time). It is preferred if the database is embedded into the application to minimize response times. The data should (preferably) fit in memory. Consistency guarantees are: causally consistent (strong EC). The data model is hierarchical, JSON like, with an emphasis on numeric values. There are no aggregations or queries on the data, only point lookups and subtree scans. The inner workings of the database is a combination of a self-orginizing overlay network and an LSM like storage engine. A node is not connected to every other node: the topology is closer to a spanning tree. That is to minimize network traffic. The API is object-handle based; the entire object is not raised into the memory; instead, once the user is reading a field, we do a lookup. That wav we minimize reads, de-serializations and GC busywork. Updates are lamport-timestamped, there is a re-sync protocol for newly joining and re-joining replicas. Overall, we take every shortcut to make the store lightweight and fast while focusing on our specific usecase (distributed counters, mainly).

Documentation

Index

Constants

View Source
const YAckOff = uint64(2)

Variables

View Source
var (
	ErrDbClosed       = errors.New("chotki: db is closed")
	ErrDirnameIsFile  = errors.New("chotki: the dirname is file")
	ErrNotImplemented = errors.New("chotki: not implemented yet")
	ErrHookNotFound   = errors.New("chotki: hook not found")
	ErrBadIRecord     = errors.New("chotki: bad id-ref record")
	ErrBadORecord     = errors.New("chotki: bad id-ref record")
	ErrBadEPacket     = errors.New("chotki: bad E packet")
	ErrBadVPacket     = errors.New("chotki: bad V packet")
	ErrBadYPacket     = errors.New("chotki: bad Y packet")
	ErrBadLPacket     = errors.New("chotki: bad L packet")
	ErrBadTPacket     = errors.New("chotki: bad T packet")
	ErrBadOPacket     = errors.New("chotki: bad O packet")
	ErrSrcUnknown     = errors.New("chotki: source unknown")
	ErrSyncUnknown    = errors.New("chotki: sync session unknown")
	ErrBadRRecord     = errors.New("chotki: bad ref record")

	ErrBadTypeDescription  = errors.New("chotki: bad type description")
	ErrUnknownFieldInAType = errors.New("chotki: unknown field for the type")
	ErrBadClass            = errors.New("chotki: bad class description")

	ErrOutOfOrder      = errors.New("chotki: order fail: sequence gap")
	ErrCausalityBroken = errors.New("chotki: order fail: refs an unknown op")
)
View Source
var (
	IdNames    = id2.ToOff(1)
	IdNodes    = id2.ToOff(2)
	IdNodeInfo = id2.ToOff(3)

	// ID from which we count user static objects
	IdLog1 = id2.ToOff(4)
)
View Source
var DiffSyncSize = prometheus.NewHistogram(prometheus.HistogramOpts{
	Namespace: "chotki",
	Name:      "diff_sync_size",
	Buckets: []float64{
		1, 10, 100, 1000,
		5000, 10000, 50000, 100000, 500000,
		1000000, 2000000, 5000000, 10000000,
		20000000, 50000000, 100000000,
		200000000, 500000000, 1000000000,
		2000000000, 5000000000,
	},
})

Size of diff sync in bytes

View Source
var DrainTime = prometheus.NewHistogramVec(prometheus.HistogramOpts{
	Namespace: "chotki",
	Name:      "drain_time",
	Buckets:   []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10, 100, 500, 1000, 5000, 10000},
}, []string{"type"})
View Source
var ErrOffsetOpId = errors.New("op id is offset")
View Source
var ErrWrongFieldType = errors.New("wrong field type")
View Source
var EventsBatchSize = prometheus.NewHistogram(prometheus.HistogramOpts{
	Namespace: "chotki",
	Name:      "batch_size",
	Buckets:   []float64{0, 1, 10, 50, 100, 500, 1000, 10000, 100000, 1000000},
})
View Source
var EventsMetric = prometheus.NewCounter(prometheus.CounterOpts{
	Namespace: "chotki",
	Name:      "packet_count",
})
View Source
var EventsOutboundMetric = prometheus.NewCounterVec(prometheus.CounterOpts{
	Namespace: "chotki",
	Name:      "outbound_packet_count",
}, []string{"name"})
View Source
var Log0 = protocol.Records{

	protocol.Record('Y',
		protocol.Record('I', rdx.ID0.ZipBytes()),
		protocol.Record('R', rdx.ID0.ZipBytes()),
	),

	protocol.Record('C',
		protocol.Record('I', id1.ZipBytes()),
		protocol.Record('R', rdx.ID0.ZipBytes()),
		rdx.Atlv(rdx.LogT{
			"M\x00Names",
		}),
	),

	protocol.Record('O',
		protocol.Record('I', id2.ZipBytes()),
		protocol.Record('R', id1.ZipBytes()),

		protocol.Record('M',
			protocol.Record('T', rdx.Ttlv("0")),
			protocol.Record('R', rdx.Rtlv(rdx.ID0)),
			protocol.Record('T', rdx.Ttlv("Global")),
			protocol.Record('R', rdx.Rtlv(id2)),
			protocol.Record('T', rdx.Ttlv("Names")),
			protocol.Record('R', rdx.Rtlv(IdNames)),
			protocol.Record('T', rdx.Ttlv("Nodes")),
			protocol.Record('R', rdx.Rtlv(IdNodes)),
		),
	),

	protocol.Record('C',
		protocol.Record('I', id3.ZipBytes()),
		protocol.Record('R', rdx.ID0.ZipBytes()),
		rdx.Atlv(rdx.LogT{
			"S\x00Name",
			"V\x00Ack",
			"S\x00Addr",
		}),
	),
}

Functions

func Exists

func Exists(dirname string) (bool, error)

Checks if the directory exists and is a directory.

func GetByHash

func GetByHash[T NativeObject](orm *ORM, cid rdx.ID, fid uint32, tlv []byte) (T, error)

Use hash index to find an object by its hash.

func SeekClass

func SeekClass[T NativeObject](orm *ORM, cid rdx.ID) iter.Seq[T]

Use full scan index to find all objects of a class.

Types

type CallHook

type CallHook struct {
	// contains filtered or unexported fields
}

type Chotki

type Chotki struct {
	IndexManager *indexes.IndexManager
	// contains filtered or unexported fields
}

Main Chotki struct

func Open

func Open(dirname string, opts Options) (*Chotki, error)

Opens a new Chotki instance.

func (*Chotki) AddHook

func (cho *Chotki) AddHook(fid rdx.ID, hook Hook)

func (*Chotki) AddToNField

func (cho *Chotki) AddToNField(ctx context.Context, fid rdx.ID, count uint64) (id rdx.ID, err error)

Increments only 'N' counters by arbitrary amount.

func (*Chotki) ApplyC

func (cho *Chotki) ApplyC(id, ref rdx.ID, body []byte, batch *pebble.Batch, calls *[]CallHook) (err error)

Handles 'C' packets which can occur from applying local changes or live sync. Creates or updates class definition. Classes are special objects. They are stored in separate key range in pebble. They also have more simplified behaviour in create/update scenarios: they are just created/replaced as is. All classes are created with rid = rdx.ID0, if you pass other ref, it should be real ref of what you want to edit.

func (*Chotki) ApplyD

func (cho *Chotki) ApplyD(id, ref rdx.ID, body []byte, batch *pebble.Batch) (err error)

During the diff sync it handles the 'D' packets which most of the time contains a single block (look at the replication protocol description). It does not immediately apply the changes to DB, instead using a batch. The batch will be applied when we finish the diffsync, when we receive the 'V' packet.

func (*Chotki) ApplyE

func (cho *Chotki) ApplyE(id, r rdx.ID, body []byte, batch *pebble.Batch, calls *[]CallHook) (err error)

Handles 'E' packets which can occur from applying local changes or live sync. Edits obkject fields. Unlike ApplyOY, it does not assume that we update whole object, as we can update individual fields. It also sets the current replica src id for FIRST/MEL types. Otherwise its just merges bytes into the batch.

func (*Chotki) ApplyH

func (cho *Chotki) ApplyH(id, ref rdx.ID, body []byte, batch *pebble.Batch) (err error)

During the diff sync it handles the 'H' packets which contains the version vector of other replica. So we put their version vector into the batch to update ours after diff sync.

func (*Chotki) ApplyOY

func (cho *Chotki) ApplyOY(lot byte, id, ref rdx.ID, body []byte, batch *pebble.Batch) (err error)

Handles 'O' packets which can occur from applying local changes or live sync. Creates objects 'O'. 'Y' are special kind of objects, unused at the moment. Typically expects an 'O' package: 0 — 'O' record that contains class rdx.ID 1 - ... - class fields First it creates 'O' field. Then it goes through the rest of the fields encoded as TLV. The only transformation it does: it sets the current replica src id for FIRST/MEL types, because historically they are not set when creating those fields (for convinience?)

func (*Chotki) ApplyV

func (cho *Chotki) ApplyV(id, ref rdx.ID, body []byte, batch *pebble.Batch) (err error)

During the diff sync it handles the 'V' packets. This packet effectively completes the diffy sync. It contains the version vector of the blocks we synced during the diff sync. We put then in the batch to update our blocks version vectors.

func (*Chotki) Broadcast

func (cho *Chotki) Broadcast(ctx context.Context, records protocol.Records, except string)

Broadcasts some records to all active replication sessions that this replica has, except one.

func (*Chotki) ClassFields

func (cho *Chotki) ClassFields(cid rdx.ID) (fields classes.Fields, err error)

Returns class fields and caches them. If class is changed cache will be invalidated.

func (*Chotki) Clock

func (cho *Chotki) Clock() rdx.Clock

Returns the clock of the Chotki instance.

func (*Chotki) Close

func (cho *Chotki) Close() error

Gracefully closes the Chotki instance.

func (*Chotki) CommitPacket

func (cho *Chotki) CommitPacket(ctx context.Context, lit byte, ref rdx.ID, body protocol.Records) (id rdx.ID, err error)

Commits records to actual storage (pebble) and broadcasts the update to all active replication sessions. Increments replica last rdx.ID and stamps this update with it. This id will be used as an ID of a new object (if it is an object), also this will be the latest seen rdx.ID in the version vector. Uses an exclusive lock, so all commits are serialized, but remember that Drain/drain calls are not. All replication sessions will call Drain in parallel.

func (*Chotki) Connect

func (cho *Chotki) Connect(addr string) error

Connects to the given address.

func (*Chotki) ConnectPool

func (cho *Chotki) ConnectPool(name string, addrs []string) error

Connects to the given address pool.

func (*Chotki) Counter

func (cho *Chotki) Counter(rid rdx.ID, offset uint64, updatePeriod time.Duration) *counters.AtomicCounter

Returns an atomic counter object that can be used to increment/decrement a counter.

func (*Chotki) Database

func (cho *Chotki) Database() *pebble.DB

Returns the database of the Chotki instance.

func (*Chotki) Directory

func (cho *Chotki) Directory() string

Returns the directory of the Chotki instance.

func (*Chotki) Disconnect

func (cho *Chotki) Disconnect(addr string) error

Disconnects from the given address.

func (*Chotki) Drain

func (cho *Chotki) Drain(ctx context.Context, recs protocol.Records) (err error)

Public for drain method with some additional metrics

func (*Chotki) DumpAll

func (cho *Chotki) DumpAll(writer io.Writer)

Dumps all objects and version vectors to the writer.

func (*Chotki) DumpObjects

func (cho *Chotki) DumpObjects(writer io.Writer)

Dumps all objects to the writer.

func (*Chotki) DumpVV

func (cho *Chotki) DumpVV(writer io.Writer)

Dumps all version vectors to the writer.

func (*Chotki) EditFieldTLV

func (cho *Chotki) EditFieldTLV(ctx context.Context, fid rdx.ID, delta []byte) (id rdx.ID, err error)

Edits the object field using TLV-encoded value.

func (*Chotki) EditObjectRDX

func (cho *Chotki) EditObjectRDX(ctx context.Context, oid rdx.ID, pairs []rdx.RDX) (id rdx.ID, err error)

Edits the object fields using string RDX representation.

func (*Chotki) GetClassTLV

func (cho *Chotki) GetClassTLV(ctx context.Context, cid rdx.ID) ([]byte, error)

Returns the TLV-encoded class definition. Only used in REPL.

func (*Chotki) GetFieldTLV

func (cho *Chotki) GetFieldTLV(id rdx.ID) (rdt byte, tlv []byte)

Returns the TLV-encoded value of the object field, given object rdx.ID with offset.

func (*Chotki) IncNField

func (cho *Chotki) IncNField(ctx context.Context, fid rdx.ID) (id rdx.ID, err error)

Increments only 'N' counters by 1.

func (*Chotki) Last

func (cho *Chotki) Last() rdx.ID

Returns the latest used rdx.ID of current replica

func (*Chotki) Listen

func (cho *Chotki) Listen(addr string) error

Starts listening on the given address.

func (*Chotki) Logger

func (cho *Chotki) Logger() utils.Logger

Returns the logger of the Chotki instance.

func (*Chotki) MapTRField

func (cho *Chotki) MapTRField(fid rdx.ID) (themap rdx.MapTR, err error)

Extracts TLV value of the field that is supposed to be a map and converts it to golang map.

func (*Chotki) Metrics

func (cho *Chotki) Metrics() []prometheus.Collector

Returns a list of prometheus collectors for the Chotki instance.

func (*Chotki) NewClass

func (cho *Chotki) NewClass(ctx context.Context, parent rdx.ID, fields ...classes.Field) (id rdx.ID, err error)

Creates or updates class definition. Remember that you can only add fields, not remove them.

func (*Chotki) NewObjectTLV

func (cho *Chotki) NewObjectTLV(ctx context.Context, tid rdx.ID, fields protocol.Records) (id rdx.ID, err error)

Thin wrapper around CommitPacket with 'O' type

func (*Chotki) ObjectFieldTLV

func (cho *Chotki) ObjectFieldTLV(fid rdx.ID) (rdt byte, tlv []byte, err error)

Returns the TLV-encoded value of the object field, given object rdx.ID with offset.

func (*Chotki) ObjectFields

func (cho *Chotki) ObjectFields(oid rdx.ID) (tid rdx.ID, decl classes.Fields, fact protocol.Records, err error)

Given object id, returns class definition and actual object fields values

func (*Chotki) ObjectIterator

func (cho *Chotki) ObjectIterator(oid rdx.ID, snap *pebble.Snapshot) *pebble.Iterator

Creates and iterator that only walks through the specific object. Will return nil if the object does not exist.

func (*Chotki) ObjectMapper

func (cho *Chotki) ObjectMapper() *ORM

Returns the new instance of the ORM object

func (*Chotki) ObjectString

func (cho *Chotki) ObjectString(oid rdx.ID) (txt string, err error)

Returns the string representation of the object.

func (*Chotki) RemoveAllHooks

func (cho *Chotki) RemoveAllHooks(fid rdx.ID)

func (*Chotki) RemoveHook

func (cho *Chotki) RemoveHook(fid rdx.ID, hook Hook) (err error)

func (*Chotki) Snapshot

func (cho *Chotki) Snapshot() pebble.Reader

Returns a new snapshot of the Chotki instance.

func (*Chotki) Source

func (cho *Chotki) Source() uint64

Returns the source id of the Chotki instance.

func (*Chotki) Unlisten

func (cho *Chotki) Unlisten(addr string) error

Stops listening on the given address.

func (*Chotki) UpdateVTree

func (cho *Chotki) UpdateVTree(id, ref rdx.ID, pb *pebble.Batch) (err error)

Updates the Vkey0, which stores version vectors for different replicas. But also updates the corresponding block (check replication protocol description) version vector. Version vector is a map: replica src id -> latest seen rdx.ID

func (*Chotki) VersionVector

func (cho *Chotki) VersionVector() (vv rdx.VV, err error)

Returns the version vector of the Chotki instance as rdx.VV structure.

func (*Chotki) WriteOptions

func (cho *Chotki) WriteOptions() *pebble.WriteOptions

Returns the write options of the Chotki instance.

type ChotkiCollector

type ChotkiCollector struct {
	// contains filtered or unexported fields
}

func NewChotkiCollector

func NewChotkiCollector(chotki *Chotki) *ChotkiCollector

func (*ChotkiCollector) Collect

func (n *ChotkiCollector) Collect(m chan<- prometheus.Metric)

func (*ChotkiCollector) Describe

func (c *ChotkiCollector) Describe(d chan<- *prometheus.Desc)

type Hook

type Hook func(cho *Chotki, id rdx.ID) error

type Merger

type Merger interface {
	// merges values, sorted old to new
	Merge(inputs [][]byte) []byte
}

type NativeObject

type NativeObject interface {
	// Read data from an iterator
	Load(field uint64, rdt byte, tlv []byte) error
	// Compare to the stored state, serialize the changes
	Store(field uint64, rdt byte, old []byte, clock rdx.Clock) (changes []byte, err error)
}

type NetCollector

type NetCollector struct {
	// contains filtered or unexported fields
}

func NewNetCollector

func NewNetCollector(net *network.Net) *NetCollector

func (*NetCollector) Collect

func (n *NetCollector) Collect(m chan<- prometheus.Metric)

func (*NetCollector) Describe

func (n *NetCollector) Describe(d chan<- *prometheus.Desc)

type ORM

type ORM struct {
	Host *Chotki
	Snap *pebble.Snapshot
	// contains filtered or unexported fields
}

func NewORM

func NewORM(host *Chotki, snap *pebble.Snapshot) *ORM

func (*ORM) Clear

func (orm *ORM) Clear() error

Clear forgets all the objects loaded; all the unsaved changes discarded

func (*ORM) Close

func (orm *ORM) Close() error

func (*ORM) FindID

func (orm *ORM) FindID(obj NativeObject) rdx.ID

Find the ID of the registered object's.

func (*ORM) GetIdByHash

func (orm *ORM) GetIdByHash(cid rdx.ID, fid uint32, tlv []byte) (rdx.ID, error)

Use hash index to find an object rdx.ID by its hash.

func (*ORM) Load

func (orm *ORM) Load(id rdx.ID, blanc NativeObject, skipFields ...uint64) (obj NativeObject, err error)

Load the object's state from the db, register the object. If an object is already registered for that id, returns the old one. The new one is not used then.

func (*ORM) New

func (orm *ORM) New(ctx context.Context, cid rdx.ID, objs ...NativeObject) (err error)

New object of the same type get persisted and registered with the ORM

func (*ORM) Object

func (orm *ORM) Object(id rdx.ID) NativeObject

Find a registered object given its id. nil if none.

func (*ORM) Save

func (orm *ORM) Save(ctx context.Context, objs ...NativeObject) (err error)

Save the registered object's changes. Much faster than SaveALl() esp if you loaded many, modified few.

func (*ORM) SaveAll

func (orm *ORM) SaveAll(ctx context.Context) (err error)

SaveAll the changed fields; this will scan the objects and their database records.

func (*ORM) SeekIds

func (orm *ORM) SeekIds(cid rdx.ID) iter.Seq[rdx.ID]

Use full scan index to find all object rdx.IDs of a class.

func (*ORM) SyncAll

func (orm *ORM) SyncAll(ctx context.Context) (err error)

Saves all the changes, updates all the objects to the current db state.

func (*ORM) UpdateAll

func (orm *ORM) UpdateAll() (err error)

UpdateAll the registered objects to the new db state

func (*ORM) UpdateObject

func (orm *ORM) UpdateObject(obj NativeObject, snap *pebble.Snapshot) error

type Options

type Options struct {
	pebble.Options

	Src                        uint64
	Name                       string
	Log1                       protocol.Records
	Logger                     utils.Logger
	PingPeriod                 time.Duration // how often should we ping neighbour replicae if its silent
	PingWait                   time.Duration // how much time we wait until pong received
	PebbleWriteOptions         *pebble.WriteOptions
	BroadcastQueueMaxSize      int // size in bytes, after reaching it all writes will block
	BroadcastQueueMinBatchSize int // reads will wait until they have enough data or timelimit expires
	// if this limit expires before read has enough data (BroadcastQueueMinBatchSize) it will return whatever it has,
	// writes will cause overflow error which will result in queue shutdown and session end
	BroadcastQueueTimeLimit    time.Duration
	ReadAccumTimeLimit         time.Duration //
	ReadMaxBufferSize          int
	ReadMinBufferSizeToProcess int
	TcpReadBufferSize          int
	TcpWriteBufferSize         int
	WriteTimeout               time.Duration
	TlsConfig                  *tls.Config
	MaxSyncDuration            time.Duration
}

func (*Options) SetDefaults

func (o *Options) SetDefaults()

type PebbleCollector

type PebbleCollector struct {
	// contains filtered or unexported fields
}

func NewPebbleCollector

func NewPebbleCollector(db *pebble.DB) *PebbleCollector

func (*PebbleCollector) Collect

func (pc *PebbleCollector) Collect(ch chan<- prometheus.Metric)

func (*PebbleCollector) Describe

func (pc *PebbleCollector) Describe(ch chan<- *prometheus.Desc)

type PebbleMergeAdaptor

type PebbleMergeAdaptor struct {
	// contains filtered or unexported fields
}

func (*PebbleMergeAdaptor) Finish

func (a *PebbleMergeAdaptor) Finish(includesBase bool) (res []byte, cl io.Closer, err error)

func (*PebbleMergeAdaptor) MergeNewer

func (a *PebbleMergeAdaptor) MergeNewer(value []byte) error

func (*PebbleMergeAdaptor) MergeOlder

func (a *PebbleMergeAdaptor) MergeOlder(value []byte) error

Directories

Path Synopsis
Provides commone chotki errors definitions.
Provides commone chotki errors definitions.
Defines Host interfaces for Chotki
Defines Host interfaces for Chotki
Package indexes provides the index management subsystem for Chotki.
Package indexes provides the index management subsystem for Chotki.
Package network provides high-performance TCP/TLS transport for the Chotki protocol.
Package network provides high-performance TCP/TLS transport for the Chotki protocol.
Implements a compact TLV (Type-Length-Value) encoding format optimized for efficiency.
Implements a compact TLV (Type-Length-Value) encoding format optimized for efficiency.
Implements the Chotki distributed synchronization protocol.
Implements the Chotki distributed synchronization protocol.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL