beamdb 0.9.2 → 0.12.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 +117 -4
- package/beam.d.ts +5 -5
- package/beam.js +20 -20
- package/beam_bg.wasm +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
- [Wire Protocol](#wire-protocol)
|
|
24
24
|
- [Configuration](#configuration)
|
|
25
25
|
- [Testing](#testing)
|
|
26
|
+
- [Benchmarks](#benchmarks)
|
|
26
27
|
- [Features](#features)
|
|
27
28
|
- [Security](#security)
|
|
28
29
|
- [Contributing](#contributing)
|
|
@@ -111,7 +112,7 @@ await init();
|
|
|
111
112
|
|
|
112
113
|
// Create a BEAM node
|
|
113
114
|
const beam = new Beam(); // in-memory (lost on reload)
|
|
114
|
-
// or: const beam =
|
|
115
|
+
// or: const beam = Beam.new_persistent(); // IndexedDB (survives reload)
|
|
115
116
|
|
|
116
117
|
// Connect to a relay server
|
|
117
118
|
beam.connect("wss://relay.example.com/ws");
|
|
@@ -125,7 +126,7 @@ beam.put_null("users.alice.deleted"); // explicit null
|
|
|
125
126
|
// Read once (returns a Promise)
|
|
126
127
|
const name = await beam.get("users.alice.name"); // "Alice"
|
|
127
128
|
|
|
128
|
-
// Subscribe to updates
|
|
129
|
+
// Subscribe to child updates (Gun.js .on() semantics)
|
|
129
130
|
beam.on("users.alice.name", (value) => {
|
|
130
131
|
console.log("name changed:", value);
|
|
131
132
|
});
|
|
@@ -180,7 +181,7 @@ beam.stop();
|
|
|
180
181
|
import init, { Beam } from "./beam.js";
|
|
181
182
|
await init();
|
|
182
183
|
|
|
183
|
-
const beam =
|
|
184
|
+
const beam = Beam.new_persistent();
|
|
184
185
|
beam.connect("wss://relay.example.com/ws");
|
|
185
186
|
|
|
186
187
|
// Writes survive page reload via IndexedDB
|
|
@@ -244,6 +245,10 @@ BEAM browser nodes are wire-compatible with Gun.js. A BEAM WASM node can:
|
|
|
244
245
|
- Verify Gun.js SEA signatures (P-256 ECDSA, double-hashed)
|
|
245
246
|
- Interoperate with the Gun.js graph format
|
|
246
247
|
|
|
248
|
+
Bidirectional compatibility is verified by Playwright E2E tests
|
|
249
|
+
(`tests/e2e/gun-beam-interop.spec.mjs`) covering Gun.js→BEAM, BEAM→Gun.js,
|
|
250
|
+
and bidirectional convergence scenarios.
|
|
251
|
+
|
|
247
252
|
## Quick Start
|
|
248
253
|
|
|
249
254
|
### Build & Run a Node
|
|
@@ -799,6 +804,7 @@ let config = Config {
|
|
|
799
804
|
my_pub: Some("x.y".into()),
|
|
800
805
|
broadcast_buffer_size: 4096,
|
|
801
806
|
ice_servers: vec!["stun:stun.l.google.com:19302".into()],
|
|
807
|
+
dedup_capacity: 100_000,
|
|
802
808
|
};
|
|
803
809
|
# }
|
|
804
810
|
```
|
|
@@ -872,7 +878,6 @@ cargo clippy -- -D warnings
|
|
|
872
878
|
# Doctests only (verifies README code examples compile)
|
|
873
879
|
cargo test --doc
|
|
874
880
|
|
|
875
|
-
# Benchmarks
|
|
876
881
|
cargo bench
|
|
877
882
|
|
|
878
883
|
# Run a specific integration test
|
|
@@ -903,6 +908,114 @@ cargo test --test wire_live -- --ignored # Layer 3: live integration (needs Nod
|
|
|
903
908
|
|
|
904
909
|
---
|
|
905
910
|
|
|
911
|
+
## Benchmarks
|
|
912
|
+
|
|
913
|
+
BEAM includes a comprehensive benchmarking suite covering relay throughput,
|
|
914
|
+
micro-benchmarks for hot-path components, and storage performance.
|
|
915
|
+
|
|
916
|
+
### Local Put Throughput
|
|
917
|
+
|
|
918
|
+
Local (non-relay) puts through the actor pipeline — measures the full
|
|
919
|
+
`Node::handle → Router::route → MemoryStorage::apply` path with no network I/O:
|
|
920
|
+
|
|
921
|
+
| Scenario | Messages | Throughput |
|
|
922
|
+
|----------|----------|------------|
|
|
923
|
+
| 1 sender × 10k | 10,000 | ~24,000–53,000 puts/sec |
|
|
924
|
+
|
|
925
|
+
Throughput varies with system load. On a dedicated machine with no
|
|
926
|
+
competing processes, expect 50,000+ puts/sec. The bottleneck is
|
|
927
|
+
`Value` cloning for broadcast channels — further gains require
|
|
928
|
+
`Arc<Value>` to make cloning a refcount bump.
|
|
929
|
+
|
|
930
|
+
### Relay Throughput
|
|
931
|
+
|
|
932
|
+
Real WebSocket connections through a memory-only relay (no disk I/O):
|
|
933
|
+
|
|
934
|
+
| Scenario | Messages | Throughput |
|
|
935
|
+
|----------|----------|------------|
|
|
936
|
+
| 1 sender × 10k | 10,000 | ~5,300 msgs/sec |
|
|
937
|
+
| 1 sender × 50k | 50,000 | ~10,600 msgs/sec |
|
|
938
|
+
| 10 senders × 5k | 50,000 | ~11,400 msgs/sec |
|
|
939
|
+
|
|
940
|
+
The relay's internal processing (parse + dedup + route + serialize) runs in
|
|
941
|
+
microseconds — the bottleneck is client-side `put().await`, not the relay.
|
|
942
|
+
|
|
943
|
+
### Micro-Benchmarks (Criterion)
|
|
944
|
+
|
|
945
|
+
| Operation | Time |
|
|
946
|
+
|-----------|------|
|
|
947
|
+
| Parse small Put JSON | 1,067 ns |
|
|
948
|
+
| Parse medium Put JSON | 2.00 µs |
|
|
949
|
+
| Serialize small Put JSON | 69 ns |
|
|
950
|
+
| Parse Get | 677 ns |
|
|
951
|
+
| Dedup check (fresh) | 274 µs |
|
|
952
|
+
| Dedup check (duplicate) | 41.8 µs |
|
|
953
|
+
| Actor mailbox send+recv | 309 µs |
|
|
954
|
+
|
|
955
|
+
### WASM Benchmarks (Node.js)
|
|
956
|
+
|
|
957
|
+
| Operation | WASM | Native | Ratio |
|
|
958
|
+
|-----------|------|--------|-------|
|
|
959
|
+
| Parse small Put | 7.9 µs | 1,067 ns | ~7.4× |
|
|
960
|
+
| Serialize small Put | 8.6 µs | 69 ns | ~125× |
|
|
961
|
+
| Parse Get | 4.5 µs | 677 ns | ~6.7× |
|
|
962
|
+
|
|
963
|
+
Run with: `wasm-pack test --node --no-default-features -- --nocapture`
|
|
964
|
+
|
|
965
|
+
### WASM Relay Throughput (Browser-only)
|
|
966
|
+
|
|
967
|
+
`web_sys::WebSocket` callbacks don't fire in Node.js `wasm-bindgen-test-runner`
|
|
968
|
+
([known limitation](https://github.com/wasm-bindgen/wasm-bindgen/issues/4921)).
|
|
969
|
+
Use the browser benchmark page to measure WASM relay throughput:
|
|
970
|
+
|
|
971
|
+
```bash
|
|
972
|
+
cargo run -- start --port 4944 --memory-storage true --redb-storage false
|
|
973
|
+
python3 -m http.server 8080 -d examples/
|
|
974
|
+
# Open http://localhost:8080/bench.html in a browser
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
Previous v0.11.0 browser results: ~115–651 msgs/sec depending on batch size.
|
|
978
|
+
|
|
979
|
+
### Browser Benchmark
|
|
980
|
+
|
|
981
|
+
An interactive benchmark page is available at `examples/bench.html`:
|
|
982
|
+
|
|
983
|
+
```bash
|
|
984
|
+
# Start a relay
|
|
985
|
+
cargo run -- start --port 4944 --memory-storage true --redb-storage false
|
|
986
|
+
|
|
987
|
+
# Serve the benchmark page
|
|
988
|
+
python3 -m http.server 8080 -d examples/
|
|
989
|
+
|
|
990
|
+
# Open in browser
|
|
991
|
+
open http://localhost:8080/bench.html
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
The browser benchmark measures:
|
|
995
|
+
- **Relay TPS**: end-to-end throughput through a real relay
|
|
996
|
+
- **Put throughput**: local WASM API fire-and-forget puts
|
|
997
|
+
- **Get throughput**: local WASM API promise resolution
|
|
998
|
+
- **Put→Get round-trip**: full local cycle
|
|
999
|
+
|
|
1000
|
+
### Running Benchmarks
|
|
1001
|
+
|
|
1002
|
+
```bash
|
|
1003
|
+
# Relay throughput (release mode required)
|
|
1004
|
+
cargo test --release --test relay_throughput_bench -- --ignored --nocapture
|
|
1005
|
+
|
|
1006
|
+
# Micro-benchmarks (hot-path components)
|
|
1007
|
+
cargo bench --bench my_benchmark -- "wire_|dup_check|actor_mailbox"
|
|
1008
|
+
|
|
1009
|
+
# Storage benchmarks
|
|
1010
|
+
cargo bench --bench my_benchmark -- "write_storm|read_storm|mixed"
|
|
1011
|
+
|
|
1012
|
+
# Live metrics endpoint (while relay is running)
|
|
1013
|
+
curl http://localhost:8080/metrics
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
See [`benches/RESULTS.md`](benches/RESULTS.md) for full results with
|
|
1017
|
+
methodology and analysis.
|
|
1018
|
+
|
|
906
1019
|
## Features
|
|
907
1020
|
|
|
908
1021
|
| Feature | Default | Enables |
|
package/beam.d.ts
CHANGED
|
@@ -115,11 +115,11 @@ export interface InitOutput {
|
|
|
115
115
|
readonly beam_put_num: (a: number, b: number, c: number, d: number) => void;
|
|
116
116
|
readonly beam_stop: (a: number) => void;
|
|
117
117
|
readonly task_worker_entry_point: (a: number, b: number) => void;
|
|
118
|
-
readonly
|
|
119
|
-
readonly
|
|
120
|
-
readonly
|
|
121
|
-
readonly
|
|
122
|
-
readonly
|
|
118
|
+
readonly __wasm_bindgen_func_elem_1109: (a: number, b: number, c: number, d: number) => void;
|
|
119
|
+
readonly __wasm_bindgen_func_elem_1123: (a: number, b: number, c: number, d: number) => void;
|
|
120
|
+
readonly __wasm_bindgen_func_elem_232: (a: number, b: number, c: number) => void;
|
|
121
|
+
readonly __wasm_bindgen_func_elem_232_2: (a: number, b: number, c: number) => void;
|
|
122
|
+
readonly __wasm_bindgen_func_elem_232_3: (a: number, b: number, c: number) => void;
|
|
123
123
|
readonly __wbindgen_export: (a: number, b: number) => number;
|
|
124
124
|
readonly __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
|
|
125
125
|
readonly __wbindgen_export3: (a: number) => void;
|
package/beam.js
CHANGED
|
@@ -290,7 +290,7 @@ function __wbg_get_imports() {
|
|
|
290
290
|
const a = state0.a;
|
|
291
291
|
state0.a = 0;
|
|
292
292
|
try {
|
|
293
|
-
return
|
|
293
|
+
return __wasm_bindgen_func_elem_1123(a, state0.b, arg0, arg1);
|
|
294
294
|
} finally {
|
|
295
295
|
state0.a = a;
|
|
296
296
|
}
|
|
@@ -308,7 +308,7 @@ function __wbg_get_imports() {
|
|
|
308
308
|
const a = state0.a;
|
|
309
309
|
state0.a = 0;
|
|
310
310
|
try {
|
|
311
|
-
return
|
|
311
|
+
return __wasm_bindgen_func_elem_1123(a, state0.b, arg0, arg1);
|
|
312
312
|
} finally {
|
|
313
313
|
state0.a = a;
|
|
314
314
|
}
|
|
@@ -432,23 +432,23 @@ function __wbg_get_imports() {
|
|
|
432
432
|
return addHeapObject(ret);
|
|
433
433
|
}, arguments); },
|
|
434
434
|
__wbindgen_cast_0000000000000001: function(arg0, arg1) {
|
|
435
|
-
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx:
|
|
436
|
-
const ret = makeMutClosure(arg0, arg1,
|
|
435
|
+
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 15, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
|
436
|
+
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_232);
|
|
437
437
|
return addHeapObject(ret);
|
|
438
438
|
},
|
|
439
439
|
__wbindgen_cast_0000000000000002: function(arg0, arg1) {
|
|
440
|
-
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx:
|
|
441
|
-
const ret = makeMutClosure(arg0, arg1,
|
|
440
|
+
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 272, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
|
441
|
+
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_1109);
|
|
442
442
|
return addHeapObject(ret);
|
|
443
443
|
},
|
|
444
444
|
__wbindgen_cast_0000000000000003: function(arg0, arg1) {
|
|
445
|
-
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("IDBVersionChangeEvent")], shim_idx:
|
|
446
|
-
const ret = makeMutClosure(arg0, arg1,
|
|
445
|
+
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("IDBVersionChangeEvent")], shim_idx: 15, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
|
446
|
+
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_232_2);
|
|
447
447
|
return addHeapObject(ret);
|
|
448
448
|
},
|
|
449
449
|
__wbindgen_cast_0000000000000004: function(arg0, arg1) {
|
|
450
|
-
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("MessageEvent")], shim_idx:
|
|
451
|
-
const ret = makeMutClosure(arg0, arg1,
|
|
450
|
+
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("MessageEvent")], shim_idx: 15, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
|
451
|
+
const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_232_3);
|
|
452
452
|
return addHeapObject(ret);
|
|
453
453
|
},
|
|
454
454
|
__wbindgen_cast_0000000000000005: function(arg0) {
|
|
@@ -475,22 +475,22 @@ function __wbg_get_imports() {
|
|
|
475
475
|
};
|
|
476
476
|
}
|
|
477
477
|
|
|
478
|
-
function
|
|
479
|
-
wasm.
|
|
478
|
+
function __wasm_bindgen_func_elem_232(arg0, arg1, arg2) {
|
|
479
|
+
wasm.__wasm_bindgen_func_elem_232(arg0, arg1, addHeapObject(arg2));
|
|
480
480
|
}
|
|
481
481
|
|
|
482
|
-
function
|
|
483
|
-
wasm.
|
|
482
|
+
function __wasm_bindgen_func_elem_232_2(arg0, arg1, arg2) {
|
|
483
|
+
wasm.__wasm_bindgen_func_elem_232_2(arg0, arg1, addHeapObject(arg2));
|
|
484
484
|
}
|
|
485
485
|
|
|
486
|
-
function
|
|
487
|
-
wasm.
|
|
486
|
+
function __wasm_bindgen_func_elem_232_3(arg0, arg1, arg2) {
|
|
487
|
+
wasm.__wasm_bindgen_func_elem_232_3(arg0, arg1, addHeapObject(arg2));
|
|
488
488
|
}
|
|
489
489
|
|
|
490
|
-
function
|
|
490
|
+
function __wasm_bindgen_func_elem_1109(arg0, arg1, arg2) {
|
|
491
491
|
try {
|
|
492
492
|
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
493
|
-
wasm.
|
|
493
|
+
wasm.__wasm_bindgen_func_elem_1109(retptr, arg0, arg1, addHeapObject(arg2));
|
|
494
494
|
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
495
495
|
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
496
496
|
if (r1) {
|
|
@@ -501,8 +501,8 @@ function __wasm_bindgen_func_elem_1101(arg0, arg1, arg2) {
|
|
|
501
501
|
}
|
|
502
502
|
}
|
|
503
503
|
|
|
504
|
-
function
|
|
505
|
-
wasm.
|
|
504
|
+
function __wasm_bindgen_func_elem_1123(arg0, arg1, arg2, arg3) {
|
|
505
|
+
wasm.__wasm_bindgen_func_elem_1123(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3));
|
|
506
506
|
}
|
|
507
507
|
|
|
508
508
|
|
package/beam_bg.wasm
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"David Newman <david.r.newman@proton.me>"
|
|
7
7
|
],
|
|
8
8
|
"description": "BEAM — distributed graph database syncing over WebSocket, WebRTC, and multicast. Successor to rod.",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.12.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|