@sovereignbase/convergent-replicated-struct 0.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,271 +1,402 @@
1
- [![npm version](https://img.shields.io/npm/v/@sovereignbase/convergent-replicated-struct)](https://www.npmjs.com/package/@sovereignbase/convergent-replicated-struct)
2
- [![CI](https://github.com/sovereignbase/convergent-replicated-struct/actions/workflows/ci.yaml/badge.svg?branch=master)](https://github.com/sovereignbase/convergent-replicated-struct/actions/workflows/ci.yaml)
3
- [![codecov](https://codecov.io/gh/sovereignbase/convergent-replicated-struct/branch/master/graph/badge.svg)](https://codecov.io/gh/sovereignbase/convergent-replicated-struct)
4
- [![license](https://img.shields.io/npm/l/@sovereignbase/convergent-replicated-struct)](LICENSE)
5
-
6
- # convergent-replicated-struct
7
-
8
- State-based CRDT for fixed-key object structs with per-field overwrite tracking.
9
-
10
- ## Compatibility
11
-
12
- - Runtimes: Node >= 20; modern browsers; Bun; Deno; Cloudflare Workers; Edge Runtime.
13
- - Module format: ESM + CommonJS.
14
- - Required globals / APIs: `EventTarget`, `CustomEvent`, `structuredClone`.
15
- - TypeScript: bundled types.
16
-
17
- ## Goals
18
-
19
- - Fixed-key replica shape defined by a default struct.
20
- - One visible value per field at any time.
21
- - Malformed ingress is ignored during hydration and merge instead of crashing the replica.
22
- - `read()`, `values()`, `entries()`, snapshots, deltas, and change payloads are detached with `structuredClone`.
23
- - Explicit `acknowledge()` and `garbageCollect()` APIs for overwrite-history compaction.
24
- - Consistent behavior across Node, browsers, and worker/edge runtimes.
25
-
26
- ## Installation
27
-
28
- ```sh
29
- npm install @sovereignbase/convergent-replicated-struct
30
- # or
31
- pnpm add @sovereignbase/convergent-replicated-struct
32
- # or
33
- yarn add @sovereignbase/convergent-replicated-struct
34
- # or
35
- bun add @sovereignbase/convergent-replicated-struct
36
- # or
37
- deno add jsr:@sovereignbase/convergent-replicated-struct
38
- # or
39
- vlt install jsr:@sovereignbase/convergent-replicated-struct
40
- ```
41
-
42
- ## Usage
43
-
44
- ### Copy-paste example
45
-
46
- ```ts
47
- import { OOStruct } from '@sovereignbase/convergent-replicated-struct'
48
-
49
- type TodoStruct = {
50
- title: string
51
- count: number
52
- meta: { done: boolean }
53
- tags: string[]
54
- }
55
-
56
- const alice = OOStruct.create<TodoStruct>({
57
- title: '',
58
- count: 0,
59
- meta: { done: false },
60
- tags: [],
61
- })
62
- const bob = OOStruct.create<TodoStruct>({
63
- title: '',
64
- count: 0,
65
- meta: { done: false },
66
- tags: [],
67
- })
68
-
69
- alice.addEventListener('delta', (event) => bob.merge(event.detail))
70
- alice.update('title', 'hello world')
71
- alice.update('meta', { done: true })
72
-
73
- console.log(bob.read('title')) // "hello world"
74
- console.log(bob.read('meta')) // { done: true }
75
- ```
76
-
77
- ### Hydrating from a snapshot
78
-
79
- ```ts
80
- import {
81
- OOStruct,
82
- type OOStructSnapshot,
83
- } from '@sovereignbase/convergent-replicated-struct'
84
-
85
- type DraftStruct = {
86
- title: string
87
- count: number
88
- }
89
-
90
- const source = new OOStruct<DraftStruct>({
91
- title: '',
92
- count: 0,
93
- })
94
- let snapshot!: OOStructSnapshot<DraftStruct>
95
-
96
- source.addEventListener(
97
- 'snapshot',
98
- (event) => {
99
- snapshot = event.detail
100
- },
101
- { once: true }
102
- )
103
-
104
- source.update('title', 'draft')
105
- source.snapshot()
106
-
107
- const restored = OOStruct.create<DraftStruct>(
108
- {
109
- title: '',
110
- count: 0,
111
- },
112
- snapshot
113
- )
114
-
115
- console.log(restored.entries()) // [['title', 'draft'], ['count', 0]]
116
- ```
117
-
118
- ### Event channels
119
-
120
- ```ts
121
- import { OOStruct } from '@sovereignbase/convergent-replicated-struct'
122
-
123
- const replica = new OOStruct({
124
- name: '',
125
- count: 0,
126
- })
127
-
128
- replica.addEventListener('delta', (event) => {
129
- console.log('delta', event.detail)
130
- })
131
-
132
- replica.addEventListener('change', (event) => {
133
- console.log('change', event.detail)
134
- })
135
-
136
- replica.addEventListener('ack', (event) => {
137
- console.log('ack', event.detail)
138
- })
139
-
140
- replica.addEventListener('snapshot', (event) => {
141
- console.log('snapshot', event.detail)
142
- })
143
- ```
144
-
145
- ### Acknowledgements and garbage collection
146
-
147
- ```ts
148
- import {
149
- OOStruct,
150
- type OOStructAck,
151
- } from '@sovereignbase/convergent-replicated-struct'
152
-
153
- type CounterStruct = {
154
- title: string
155
- count: number
156
- }
157
-
158
- const left = new OOStruct<CounterStruct>({
159
- title: '',
160
- count: 0,
161
- })
162
- const right = new OOStruct<CounterStruct>({
163
- title: '',
164
- count: 0,
165
- })
166
-
167
- const frontiers: Array<OOStructAck<CounterStruct>> = []
168
-
169
- left.addEventListener(
170
- 'ack',
171
- (event) => {
172
- frontiers.push(event.detail)
173
- },
174
- { once: true }
175
- )
176
-
177
- right.addEventListener(
178
- 'ack',
179
- (event) => {
180
- frontiers.push(event.detail)
181
- },
182
- { once: true }
183
- )
184
-
185
- left.acknowledge()
186
- right.acknowledge()
187
-
188
- left.garbageCollect(frontiers)
189
- right.garbageCollect(frontiers)
190
- ```
191
-
192
- ## Runtime behavior
193
-
194
- ### Validation and errors
195
-
196
- Local API misuse throws `OOStructError` with stable error codes:
197
-
198
- - `DEFAULTS_NOT_CLONEABLE`
199
- - `VALUE_NOT_CLONEABLE`
200
- - `VALUE_TYPE_MISMATCH`
201
-
202
- Hydration and merge are ingress-tolerant: malformed top-level payloads, unknown keys, malformed field entries, invalid UUIDs, invalid overwrite members, and mismatched runtime kinds are ignored instead of throwing.
203
-
204
- ### Safety and copying semantics
205
-
206
- - Constructor defaults must be `structuredClone`-compatible.
207
- - `read()`, `values()`, and `entries()` return detached clones.
208
- - `delta`, `change`, and `snapshot` event payloads are detached from live state.
209
- - `update()` stores a cloned value, so later caller-side mutation does not mutate replica state through shared references.
210
-
211
- ### Convergence and compaction
212
-
213
- - The convergence guarantee is the resolved live struct state.
214
- - Internal overwrite history may differ between replicas after acknowledgement-based garbage collection while the resolved live state still converges.
215
- - `garbageCollect()` compacts overwritten identifiers that are below the smallest acknowledgement frontier for a key while preserving the active predecessor link.
216
-
217
- ## Tests
218
-
219
- - Suite: unit, integration, and end-to-end runtime tests.
220
- - Node test runner: `node --test` for unit and integration suites.
221
- - Coverage: `c8` with 100% statements / branches / functions / lines on built `dist/**/*.js`.
222
- - E2E runtimes: Node ESM, Node CJS, Bun ESM, Bun CJS, Deno ESM, Cloudflare Workers ESM, Edge Runtime ESM.
223
- - Browser E2E: Chromium, Firefox, WebKit, mobile Chrome, mobile Safari via Playwright.
224
- - Current status: `npm run test` passes on Node 22.14.0 (`win32 x64`).
225
-
226
- ## Benchmarks
227
-
228
- How it was run:
229
-
230
- ```sh
231
- node benchmark/bench.js
232
- ```
233
-
234
- Environment: Node 22.14.0 (`win32 x64`)
235
-
236
- | Benchmark | Result |
237
- | ----------------------------- | ------------------------- |
238
- | constructor empty | 44,359 ops/s (2254.3 ms) |
239
- | constructor hydrate x64 | 19,610 ops/s (255.0 ms) |
240
- | constructor hydrate x256 | 8,088 ops/s (247.3 ms) |
241
- | constructor hydrate x1024 | 1,724 ops/s (290.0 ms) |
242
- | create() empty | 49,874 ops/s (2005.1 ms) |
243
- | create() hydrate x256 | 6,886 ops/s (290.4 ms) |
244
- | read primitive | 846,289 ops/s (236.3 ms) |
245
- | read object | 298,983 ops/s (668.9 ms) |
246
- | read array | 278,710 ops/s (717.6 ms) |
247
- | keys() | 32,349,896 ops/s (6.2 ms) |
248
- | values() | 103,489 ops/s (966.3 ms) |
249
- | entries() | 110,300 ops/s (906.6 ms) |
250
- | snapshot() | 65,513 ops/s (305.3 ms) |
251
- | acknowledge() | 536,890 ops/s (93.1 ms) |
252
- | update string | 29,547 ops/s (1692.2 ms) |
253
- | update number | 30,591 ops/s (1634.5 ms) |
254
- | update object | 22,114 ops/s (2261.0 ms) |
255
- | update array | 24,763 ops/s (2019.1 ms) |
256
- | delete(key) | 8,352 ops/s (5986.8 ms) |
257
- | delete() reset all | 6,836 ops/s (2925.5 ms) |
258
- | merge direct successor | 32,541 ops/s (1536.5 ms) |
259
- | merge stale conflict | 30,995 ops/s (645.3 ms) |
260
- | merge hydrate snapshot x256 | 5,748 ops/s (869.9 ms) |
261
- | merge noop duplicate | 7,576 ops/s (6600.1 ms) |
262
- | garbageCollect() x512 history | 3,111 ops/s (1607.0 ms) |
263
- | add/remove listener roundtrip | 49,005 ops/s (4081.2 ms) |
264
- | update with listeners | 25,120 ops/s (1194.3 ms) |
265
- | merge with listeners | 31,649 ops/s (631.9 ms) |
266
-
267
- Results vary by machine, runtime version, and payload shape.
268
-
269
- ## License
270
-
271
- Apache-2.0
1
+ [![npm version](https://img.shields.io/npm/v/@sovereignbase/convergent-replicated-list)](https://www.npmjs.com/package/@sovereignbase/convergent-replicated-struct)
2
+ [![CI](https://github.com/sovereignbase/convergent-replicated-struct/actions/workflows/ci.yaml/badge.svg?branch=master)](https://github.com/sovereignbase/convergent-replicated-struct/actions/workflows/ci.yaml)
3
+ [![codecov](https://codecov.io/gh/sovereignbase/convergent-replicated-struct/branch/master/graph/badge.svg)](https://codecov.io/gh/sovereignbase/convergent-replicated-struct)
4
+ [![license](https://img.shields.io/npm/l/@sovereignbase/convergent-replicated-struct)](LICENSE)
5
+
6
+ # convergent-replicated-struct
7
+
8
+ Convergent Replicated Struct (CR-Struct), a delta CRDT for an fixed-key object structs.
9
+
10
+ ## Compatibility
11
+
12
+ - Runtimes: Node >= 20, modern browsers, Bun, Deno, Cloudflare Workers, Edge Runtime.
13
+ - Module format: ESM + CommonJS.
14
+ - Required globals / APIs: `EventTarget`, `CustomEvent`, `structuredClone`.
15
+ - TypeScript: bundled types.
16
+
17
+ ## Goals
18
+
19
+ - Deterministic convergence of the live struct projection under asynchronous gossip delivery.
20
+ - Consistent behavior across Node, browsers, worker, and edge runtimes.
21
+ - Garbage collection possibility without breaking live-view convergence.
22
+ - Event-driven API
23
+
24
+ ## Installation
25
+
26
+ ```sh
27
+ npm install @sovereignbase/convergent-replicated-struct
28
+ # or
29
+ pnpm add @sovereignbase/convergent-replicated-struct
30
+ # or
31
+ yarn add @sovereignbase/convergent-replicated-struct
32
+ # or
33
+ bun add @sovereignbase/convergent-replicated-struct
34
+ # or
35
+ deno add jsr:@sovereignbase/convergent-replicated-struct
36
+ # or
37
+ vlt install jsr:@sovereignbase/convergent-replicated-struct
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ ### Copy-paste example
43
+
44
+ ```ts
45
+ import {
46
+ CRStruct,
47
+ type CRStructSnapshot,
48
+ } from '@sovereignbase/convergent-replicated-struct'
49
+
50
+ type MetaStruct = {
51
+ done: boolean
52
+ }
53
+
54
+ type TodoStruct = {
55
+ title: string
56
+ count: number
57
+ meta: CRStructSnapshot<MetaStruct>
58
+ tags: string[]
59
+ }
60
+
61
+ const aliceMeta = new CRStruct<MetaStruct>({done: false})
62
+
63
+ const alice = new CRStruct<TodoStruct>({
64
+ title: '',
65
+ count: 0,
66
+ meta: aliceMeta.toJSON()
67
+ tags: [],
68
+ })
69
+
70
+ const bobMeta = new CRStruct<MetaStruct>({done: false})
71
+
72
+ const bob = new CRStruct<TodoStruct>({
73
+ title: '',
74
+ count: 0,
75
+ meta: bobMeta.toJSON()
76
+ tags: [],
77
+ })
78
+
79
+ alice.addEventListener('delta', (event) => {
80
+ bob.merge(event.detail)
81
+ })
82
+
83
+ aliceMeta.done = true
84
+
85
+ alice.title = 'hello world'
86
+ alice.meta = aliceMeta.toJSON()
87
+
88
+ console.log(bob.title) // 'hello world'
89
+ console.log(bobMeta.done) // true
90
+ ```
91
+
92
+ ### Hydrating from a snapshot
93
+
94
+ ```ts
95
+ import {
96
+ CRStruct,
97
+ type CRStructSnapshot,
98
+ } from '@sovereignbase/convergent-replicated-struct'
99
+
100
+ type DraftStruct = {
101
+ title: string
102
+ count: number
103
+ }
104
+
105
+ const source = new CRStruct<DraftStruct>({
106
+ title: '',
107
+ count: 0,
108
+ })
109
+ let snapshot!: CRStructSnapshot<DraftStruct>
110
+
111
+ source.addEventListener('snapshot', (event) => {
112
+ localStorage.setItem('snapshot', JSON.stringify(event.detail))
113
+ })
114
+
115
+ source.title = 'draft'
116
+ source.snapshot()
117
+
118
+ const restored = new CRStruct<DraftStruct>(
119
+ { title: '', count: 0 },
120
+ JSON.parse(localStorage.getItem('snapshot'))
121
+ )
122
+
123
+ console.log(restored.entries()) // [['title', 'draft'], ['count', 0]]
124
+ ```
125
+
126
+ ### Event channels
127
+
128
+ ```ts
129
+ import { CRStruct } from '@sovereignbase/convergent-replicated-struct'
130
+
131
+ const replica = new CRStruct({
132
+ name: '',
133
+ count: 0,
134
+ })
135
+
136
+ replica.addEventListener('delta', (event) => {
137
+ console.log('delta', event.detail)
138
+ })
139
+
140
+ replica.addEventListener('change', (event) => {
141
+ console.log('change', event.detail)
142
+ })
143
+
144
+ replica.addEventListener('ack', (event) => {
145
+ console.log('ack', event.detail)
146
+ })
147
+
148
+ replica.addEventListener('snapshot', (event) => {
149
+ console.log('snapshot', event.detail)
150
+ })
151
+
152
+ replica.name = 'alice'
153
+ delete replica.name
154
+ replica.snapshot()
155
+ replica.acknowledge()
156
+ ```
157
+
158
+ ### Iteration and JSON serialization
159
+
160
+ ```ts
161
+ import { CRStruct } from '@sovereignbase/convergent-replicated-struct'
162
+
163
+ const struct = new CRStruct({
164
+ givenName: '',
165
+ familyName: '',
166
+ })
167
+
168
+ struct.givenName = 'Jori'
169
+ struct.familyName = 'Lehtinen'
170
+
171
+ for (const key in struct) console.log(key)
172
+ for (const [key, val] of struct) console.log(key, val)
173
+ console.log(struct.keys())
174
+ console.log(struct.values())
175
+ console.log(struct.entries())
176
+ console.log(struct.clone())
177
+ ```
178
+
179
+ Direct property reads, `for...of`, `values()`, `entries()`, and `clone()`
180
+ return detached copies of visible values. Mutating those returned values does
181
+ not mutate the underlying replica state.
182
+
183
+ ### Acknowledgements and garbage collection
184
+
185
+ ```ts
186
+ import {
187
+ CRStruct,
188
+ type CRStructAck,
189
+ } from '@sovereignbase/convergent-replicated-struct'
190
+
191
+ type CounterStruct = {
192
+ title: string
193
+ count: number
194
+ }
195
+
196
+ const alice = new CRStruct<CounterStruct>({
197
+ title: '',
198
+ count: 0,
199
+ })
200
+ const bob = new CRStruct<CounterStruct>({
201
+ title: '',
202
+ count: 0,
203
+ })
204
+
205
+ const frontiers = new Map<string, CRStructAck<CounterStruct>>()
206
+
207
+ alice.addEventListener('delta', (event) => {
208
+ bob.merge(event.detail)
209
+ })
210
+
211
+ bob.addEventListener('delta', (event) => {
212
+ alice.merge(event.detail)
213
+ })
214
+
215
+ alice.addEventListener('ack', (event) => {
216
+ frontiers.set('alice', event.detail)
217
+ })
218
+
219
+ bob.addEventListener('ack', (event) => {
220
+ frontiers.set('bob', event.detail)
221
+ })
222
+
223
+ alice.title = 'x'
224
+ alice.title = 'y'
225
+ delete alice.title
226
+
227
+ alice.acknowledge()
228
+ bob.acknowledge()
229
+
230
+ alice.garbageCollect([...frontiers.values()])
231
+ bob.garbageCollect([...frontiers.values()])
232
+ ```
233
+
234
+ ### Advanced exports
235
+
236
+ If you need to build your own fixed-key CRDT binding instead of using the
237
+ high-level `CRStruct` class, the package also exports the core CRUD and MAGS
238
+ functions together with the replica and payload types.
239
+
240
+ Those low-level exports let you build custom struct abstractions, protocol
241
+ wrappers, or framework-specific bindings while preserving the same convergence
242
+ rules as the default `CRStruct` binding.
243
+
244
+ ```ts
245
+ import {
246
+ __create,
247
+ __update,
248
+ __merge,
249
+ __snapshot,
250
+ type CRStructDelta,
251
+ type CRStructSnapshot,
252
+ } from '@sovereignbase/convergent-replicated-struct'
253
+
254
+ type DraftStruct = {
255
+ title: string
256
+ count: number
257
+ }
258
+
259
+ const defaults: DraftStruct = {
260
+ title: '',
261
+ count: 0,
262
+ }
263
+
264
+ const replica = __create(defaults)
265
+ const local = __update('title', 'draft', replica)
266
+
267
+ if (local) {
268
+ const outgoing: CRStructDelta<DraftStruct> = local.delta
269
+ const remoteChange = __merge(outgoing, replica)
270
+
271
+ console.log(remoteChange)
272
+ }
273
+
274
+ const snapshot: CRStructSnapshot<DraftStruct> = __snapshot(replica)
275
+ console.log(snapshot)
276
+ ```
277
+
278
+ The intended split is:
279
+
280
+ - `__create`, `__read`, `__update`, `__delete` for local replica mutations.
281
+ - `__merge`, `__acknowledge`, `__garbageCollect`, `__snapshot` for gossip,
282
+ compaction, and serialization.
283
+ - `CRStruct` when you want the default event-driven class API.
284
+
285
+ ## Runtime behavior
286
+
287
+ ### Validation and errors
288
+
289
+ Low-level exports can throw `CRStructError` with stable error codes:
290
+
291
+ - `DEFAULTS_NOT_CLONEABLE`
292
+ - `VALUE_NOT_CLONEABLE`
293
+ - `VALUE_TYPE_MISMATCH`
294
+
295
+ Ingress stays tolerant:
296
+
297
+ - malformed top-level merge payloads are ignored
298
+ - malformed snapshot values are dropped during hydration
299
+ - unknown keys are ignored
300
+ - invalid UUIDs and malformed field entries are ignored
301
+ - mismatched runtime kinds do not break live-state convergence
302
+
303
+ ### Safety and copying semantics
304
+
305
+ - Snapshots are serializable full-state payloads keyed by field name.
306
+ - Deltas are serializable gossip payloads keyed by field name.
307
+ - `change` is a minimal field-keyed visible patch.
308
+ - `toJSON()` returns a detached serializable snapshot.
309
+ - Direct property reads, `for...of`, `values()`, `entries()`, and `clone()`
310
+ expose detached copies of visible values rather than mutable references into
311
+ replica state.
312
+ - Property assignment, `delete`, `clear()`, `merge()`, `snapshot()`,
313
+ `acknowledge()`, and `garbageCollect()` all operate on the live struct
314
+ projection.
315
+
316
+ ### Convergence and compaction
317
+
318
+ - The convergence target is the live struct projection, not identical internal
319
+ tombstone sets.
320
+ - Tombstones remain until acknowledgement frontiers make them safe to collect.
321
+ - Garbage collection compacts overwritten identifiers below the smallest valid
322
+ acknowledgement frontier for a field while preserving the active predecessor
323
+ link.
324
+ - Internal overwrite history may differ between replicas after
325
+ acknowledgement-based garbage collection while the resolved live struct still
326
+ converges.
327
+
328
+ ## Tests
329
+
330
+ ```sh
331
+ npm run test
332
+ ```
333
+
334
+ What the current test suite covers:
335
+
336
+ - Coverage on built `dist/**/*.js`: `100%` statements, `100%` branches,
337
+ `100%` functions, and `100%` lines via `c8`.
338
+ - Public `CRStruct` surface: proxy property access, deletes, `clear()`,
339
+ iteration, events, and JSON / inspect behavior.
340
+ - Core edge paths and hostile ingress handling for `__create`, `__read`,
341
+ `__update`, `__delete`, `__merge`, `__snapshot`, `__acknowledge`, and
342
+ `__garbageCollect`.
343
+ - Snapshot hydration independent of field order, acknowledgement and garbage
344
+ collection recovery, and deterministic multi-replica gossip scenarios.
345
+ - End-to-end runtime matrix for:
346
+ - Node ESM
347
+ - Node CJS
348
+ - Bun ESM
349
+ - Bun CJS
350
+ - Deno ESM
351
+ - Cloudflare Workers ESM
352
+ - Edge Runtime ESM
353
+ - Browsers via Playwright: Chromium, Firefox, WebKit, mobile Chrome, mobile Safari
354
+ - Current status: `npm run test` passes on Node `v22.14.0` (`win32 x64`).
355
+
356
+ ## Benchmarks
357
+
358
+ ```sh
359
+ npm run bench
360
+ ```
361
+
362
+ The benchmark runner currently uses:
363
+
364
+ - `HISTORY_DEPTH = 5_000`
365
+ - `RUN_TIMES = 250`
366
+ - output columns: `group`, `scenario`, `n`, `ops`, `ms`, `ms/op`, `ops/sec`
367
+
368
+ Last measured on Node `v22.14.0` (`win32 x64`):
369
+
370
+ | group | scenario | n | ops | ms | ms/op | ops/sec |
371
+ | ------- | -------------------------------- | ----: | --: | -----: | ----: | ---------: |
372
+ | `crud` | `create / hydrate snapshot` | 5,000 | 250 | 714.80 | 2.86 | 349.75 |
373
+ | `crud` | `read / primitive field` | 5,000 | 250 | 0.55 | 0.00 | 450,531.63 |
374
+ | `crud` | `read / object field` | 5,000 | 250 | 0.83 | 0.00 | 301,568.15 |
375
+ | `crud` | `update / overwrite string` | 5,000 | 250 | 5.77 | 0.02 | 43,291.54 |
376
+ | `crud` | `update / overwrite object` | 5,000 | 250 | 4.79 | 0.02 | 52,198.61 |
377
+ | `crud` | `delete / reset single field` | 5,000 | 250 | 3.67 | 0.01 | 68,162.61 |
378
+ | `crud` | `delete / reset all fields` | 5,000 | 250 | 18.86 | 0.08 | 13,253.95 |
379
+ | `mags` | `snapshot` | 5,000 | 250 | 7.80 | 0.03 | 32,062.38 |
380
+ | `mags` | `acknowledge` | 5,000 | 250 | 39.72 | 0.16 | 6,294.04 |
381
+ | `mags` | `garbage collect` | 5,000 | 250 | 260.93 | 1.04 | 958.12 |
382
+ | `mags` | `merge ordered deltas` | 5,000 | 250 | 204.53 | 0.82 | 1,222.32 |
383
+ | `mags` | `merge direct successor` | 5,000 | 250 | 1.46 | 0.01 | 171,385.48 |
384
+ | `mags` | `merge shuffled gossip` | 5,000 | 250 | 263.91 | 1.06 | 947.29 |
385
+ | `mags` | `merge stale conflict` | 5,000 | 250 | 2.11 | 0.01 | 118,315.19 |
386
+ | `class` | `constructor / hydrate snapshot` | 5,000 | 250 | 781.32 | 3.13 | 319.97 |
387
+ | `class` | `property read / primitive` | 5,000 | 250 | 0.45 | 0.00 | 559,659.73 |
388
+ | `class` | `property read / object` | 5,000 | 250 | 0.95 | 0.00 | 262,687.82 |
389
+ | `class` | `property write / string` | 5,000 | 250 | 5.04 | 0.02 | 49,613.02 |
390
+ | `class` | `property write / object` | 5,000 | 250 | 8.57 | 0.03 | 29,157.24 |
391
+ | `class` | `delete property` | 5,000 | 250 | 4.80 | 0.02 | 52,128.95 |
392
+ | `class` | `clear()` | 5,000 | 250 | 15.35 | 0.06 | 16,283.14 |
393
+ | `class` | `snapshot` | 5,000 | 250 | 9.49 | 0.04 | 26,356.29 |
394
+ | `class` | `acknowledge` | 5,000 | 250 | 45.49 | 0.18 | 5,495.59 |
395
+ | `class` | `garbage collect` | 5,000 | 250 | 162.70 | 0.65 | 1,536.53 |
396
+ | `class` | `merge ordered deltas` | 5,000 | 250 | 193.20 | 0.77 | 1,293.98 |
397
+ | `class` | `merge direct successor` | 5,000 | 250 | 2.90 | 0.01 | 86,331.93 |
398
+ | `class` | `merge shuffled gossip` | 5,000 | 250 | 264.43 | 1.06 | 945.44 |
399
+
400
+ ## License
401
+
402
+ Apache-2.0