@peerbit/shared-log 1.0.2
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/LICENSE +202 -0
- package/README.md +3 -0
- package/lib/esm/exchange-heads.d.ts +42 -0
- package/lib/esm/exchange-heads.js +123 -0
- package/lib/esm/exchange-heads.js.map +1 -0
- package/lib/esm/exchange-replication.d.ts +14 -0
- package/lib/esm/exchange-replication.js +215 -0
- package/lib/esm/exchange-replication.js.map +1 -0
- package/lib/esm/index.d.ts +68 -0
- package/lib/esm/index.js +411 -0
- package/lib/esm/index.js.map +1 -0
- package/lib/esm/message.d.ts +2 -0
- package/lib/esm/message.js +13 -0
- package/lib/esm/message.js.map +1 -0
- package/lib/esm/package.json +3 -0
- package/lib/esm/role.d.ts +13 -0
- package/lib/esm/role.js +42 -0
- package/lib/esm/role.js.map +1 -0
- package/package.json +45 -0
- package/src/exchange-heads.ts +109 -0
- package/src/exchange-replication.ts +206 -0
- package/src/index.ts +596 -0
- package/src/message.ts +4 -0
- package/src/role.ts +26 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { variant, option, field, vec, fixedArray } from "@dao-xyz/borsh";
|
|
2
|
+
import { Entry } from "@peerbit/log";
|
|
3
|
+
import { Log } from "@peerbit/log";
|
|
4
|
+
import { logger as loggerFn } from "@peerbit/logger";
|
|
5
|
+
import { TransportMessage } from "./message.js";
|
|
6
|
+
|
|
7
|
+
const logger = loggerFn({ module: "exchange-heads" });
|
|
8
|
+
|
|
9
|
+
export class MinReplicas {
|
|
10
|
+
get value(): number {
|
|
11
|
+
throw new Error("Not implemented");
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@variant(0)
|
|
16
|
+
export class AbsolutMinReplicas extends MinReplicas {
|
|
17
|
+
_value: number;
|
|
18
|
+
constructor(value: number) {
|
|
19
|
+
super();
|
|
20
|
+
this._value = value;
|
|
21
|
+
}
|
|
22
|
+
get value() {
|
|
23
|
+
return this._value;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* This thing allows use to faster sync since we can provide
|
|
29
|
+
* references that can be read concurrently to
|
|
30
|
+
* the entry when doing Log.fromEntry or Log.fromEntryHash
|
|
31
|
+
*/
|
|
32
|
+
@variant(0)
|
|
33
|
+
export class EntryWithRefs<T> {
|
|
34
|
+
@field({ type: Entry })
|
|
35
|
+
entry: Entry<T>;
|
|
36
|
+
|
|
37
|
+
@field({ type: vec(Entry) })
|
|
38
|
+
references: Entry<T>[]; // are some parents to the entry
|
|
39
|
+
|
|
40
|
+
constructor(properties: { entry: Entry<T>; references: Entry<T>[] }) {
|
|
41
|
+
this.entry = properties.entry;
|
|
42
|
+
this.references = properties.references;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@variant([0, 0])
|
|
47
|
+
export class ExchangeHeadsMessage<T> extends TransportMessage {
|
|
48
|
+
@field({ type: vec(EntryWithRefs) })
|
|
49
|
+
heads: EntryWithRefs<T>[];
|
|
50
|
+
|
|
51
|
+
@field({ type: option(MinReplicas) })
|
|
52
|
+
minReplicas?: MinReplicas;
|
|
53
|
+
|
|
54
|
+
@field({ type: fixedArray("u8", 4) })
|
|
55
|
+
reserved: Uint8Array = new Uint8Array(4);
|
|
56
|
+
|
|
57
|
+
constructor(props: {
|
|
58
|
+
logId: Uint8Array;
|
|
59
|
+
heads: EntryWithRefs<T>[];
|
|
60
|
+
minReplicas?: MinReplicas;
|
|
61
|
+
}) {
|
|
62
|
+
super();
|
|
63
|
+
this.heads = props.heads;
|
|
64
|
+
this.minReplicas = props.minReplicas;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@variant([0, 1])
|
|
69
|
+
export class RequestHeadsMessage extends TransportMessage {
|
|
70
|
+
@field({ type: "string" })
|
|
71
|
+
address: string;
|
|
72
|
+
|
|
73
|
+
constructor(props: { topic: string; address: string }) {
|
|
74
|
+
super();
|
|
75
|
+
if (props) {
|
|
76
|
+
this.address = props.address;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const createExchangeHeadsMessage = async (
|
|
82
|
+
log: Log<any>,
|
|
83
|
+
heads: Entry<any>[],
|
|
84
|
+
includeReferences: boolean
|
|
85
|
+
) => {
|
|
86
|
+
const headsSet = new Set(heads);
|
|
87
|
+
const headsWithRefs = await Promise.all(
|
|
88
|
+
heads.map(async (head) => {
|
|
89
|
+
const refs = !includeReferences
|
|
90
|
+
? []
|
|
91
|
+
: (
|
|
92
|
+
await log.getReferenceSamples(head, {
|
|
93
|
+
pointerCount: 8,
|
|
94
|
+
memoryLimit: 1e6 / heads.length,
|
|
95
|
+
})
|
|
96
|
+
) // 1mb total limit split on all heads
|
|
97
|
+
.filter((r) => !headsSet.has(r)); // pick a proportional amount of refs so we can efficiently load the log. TODO should be equidistant for good performance?
|
|
98
|
+
return new EntryWithRefs({
|
|
99
|
+
entry: head,
|
|
100
|
+
references: refs,
|
|
101
|
+
});
|
|
102
|
+
})
|
|
103
|
+
);
|
|
104
|
+
logger.debug(`Send latest heads of '${log.id}'`);
|
|
105
|
+
return new ExchangeHeadsMessage({
|
|
106
|
+
logId: log.id!,
|
|
107
|
+
heads: headsWithRefs,
|
|
108
|
+
});
|
|
109
|
+
};
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { variant, field, option, vec } from "@dao-xyz/borsh";
|
|
2
|
+
import { TransportMessage } from "./message.js";
|
|
3
|
+
|
|
4
|
+
export const WAIT_FOR_PEERS_TIME = 5000;
|
|
5
|
+
|
|
6
|
+
@variant([2, 0])
|
|
7
|
+
export class ReplicatorInfo extends TransportMessage {
|
|
8
|
+
@field({ type: option("string") })
|
|
9
|
+
fromId?: string;
|
|
10
|
+
|
|
11
|
+
@field({ type: "string" })
|
|
12
|
+
topic: string;
|
|
13
|
+
|
|
14
|
+
@field({ type: "u32" })
|
|
15
|
+
store: number; // address
|
|
16
|
+
|
|
17
|
+
@field({ type: vec("string") })
|
|
18
|
+
heads?: string[]; // address
|
|
19
|
+
|
|
20
|
+
constructor(props?: {
|
|
21
|
+
fromId?: string;
|
|
22
|
+
topic: string;
|
|
23
|
+
store: number;
|
|
24
|
+
heads?: string[];
|
|
25
|
+
}) {
|
|
26
|
+
super();
|
|
27
|
+
if (props) {
|
|
28
|
+
this.fromId = props.fromId;
|
|
29
|
+
this.topic = props.topic;
|
|
30
|
+
this.store = props.store;
|
|
31
|
+
this.heads = props.heads;
|
|
32
|
+
/* this.allowForks = props.allowForks; */
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* @variant([2, 1])
|
|
38
|
+
export class RequestReplicatorInfo extends ProtocolMessage {
|
|
39
|
+
@field({ type: "string" })
|
|
40
|
+
id: string;
|
|
41
|
+
|
|
42
|
+
@field({ type: "string" })
|
|
43
|
+
topic: string;
|
|
44
|
+
|
|
45
|
+
@field({ type: "string" })
|
|
46
|
+
address: string; // address
|
|
47
|
+
|
|
48
|
+
@field({ type: vec("string") })
|
|
49
|
+
heads: string[];
|
|
50
|
+
|
|
51
|
+
constructor(props?: {
|
|
52
|
+
topic: string;
|
|
53
|
+
address: Address | string;
|
|
54
|
+
heads: string[];
|
|
55
|
+
}) {
|
|
56
|
+
super();
|
|
57
|
+
if (props) {
|
|
58
|
+
this.id = uuid();
|
|
59
|
+
this.topic = props.topic;
|
|
60
|
+
this.address =
|
|
61
|
+
typeof props.address === "string"
|
|
62
|
+
? props.address
|
|
63
|
+
: props.address.toString();
|
|
64
|
+
this.heads = props.heads;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
*/
|
|
69
|
+
/* export interface PeerInfoWithMeta {
|
|
70
|
+
peerInfo: ReplicatorInfo;
|
|
71
|
+
publicKey: PublicSignKey;
|
|
72
|
+
} */
|
|
73
|
+
/* return new PeerInfo({
|
|
74
|
+
key: this._shard.peer.orbitDB.identity,
|
|
75
|
+
addresses: (await this._shard.peer.node.id()).addresses.map(x => x.toString()),
|
|
76
|
+
memoryLeft: v8.getHeapStatistics().total_available_size//v8
|
|
77
|
+
}) */
|
|
78
|
+
|
|
79
|
+
/* export const createEmitHealthCheckJob = (properties: { stores: () => string[] | undefined, subscribingForReplication: (topic: string) => boolean }, replicationTopic: string, publish: (topic: string, message: Uint8Array) => Promise<void>, isOnline: () => boolean, controller: AbortController, sign: (bytes: Uint8Array) => Promise<{ signature: Uint8Array, publicKey: PublicKey }>, encryption: PublicKeyEncryption) => {
|
|
80
|
+
|
|
81
|
+
const emitHealthcheck = async (): Promise<void> => {
|
|
82
|
+
const s = properties.stores();
|
|
83
|
+
if (!s || s.length === 0) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const signedMessage = await new MaybeSigned({
|
|
87
|
+
data: serialize(new PeerInfo({
|
|
88
|
+
replicationTopic,
|
|
89
|
+
stores: s,
|
|
90
|
+
subscribingForReplication: properties.subscribingForReplication(replicationTopic),
|
|
91
|
+
memoryLeft: v8.getHeapStatistics().total_available_size//v8
|
|
92
|
+
|
|
93
|
+
}))
|
|
94
|
+
}).sign(sign)
|
|
95
|
+
const decryptedMessage = new DecryptedThing({
|
|
96
|
+
data: serialize(signedMessage)
|
|
97
|
+
})// TODO add encryption .init(encryption).encrypt(lala)
|
|
98
|
+
|
|
99
|
+
return publish(replicationTopic, serialize(decryptedMessage))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const task = async () => {
|
|
103
|
+
await emitHealthcheck();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const cron = async () => {
|
|
107
|
+
let stop = false;
|
|
108
|
+
let promise: Promise<any> = undefined;
|
|
109
|
+
let delayStopper: () => void | undefined = undefined;
|
|
110
|
+
controller.signal.addEventListener("abort", async () => {
|
|
111
|
+
stop = true;
|
|
112
|
+
if (delayStopper)
|
|
113
|
+
delayStopper();
|
|
114
|
+
await promise;
|
|
115
|
+
});
|
|
116
|
+
while (isOnline() && !stop) { //
|
|
117
|
+
promise = task();
|
|
118
|
+
await promise;
|
|
119
|
+
await delay(EMIT_HEALTHCHECK_INTERVAL, { stopperCallback: (stopper) => { delayStopper = stopper } }); // some delay
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return cron;
|
|
123
|
+
}
|
|
124
|
+
*/
|
|
125
|
+
|
|
126
|
+
/* export const requestPeerInfo = async (
|
|
127
|
+
serializedRequest: Uint8Array,
|
|
128
|
+
replicationTopic: string,
|
|
129
|
+
publish: (topic: string, message: Uint8Array) => Promise<void>,
|
|
130
|
+
identity: Identity
|
|
131
|
+
) => {
|
|
132
|
+
const signedMessage = await new MaybeSigned({
|
|
133
|
+
data: serializedRequest,
|
|
134
|
+
}).sign(async (data) => {
|
|
135
|
+
return {
|
|
136
|
+
publicKey: identity.publicKey,
|
|
137
|
+
signature: await identity.sign(data),
|
|
138
|
+
};
|
|
139
|
+
});
|
|
140
|
+
const decryptedMessage = new DecryptedThing({
|
|
141
|
+
data: serialize(signedMessage),
|
|
142
|
+
}); // TODO add encryption .init(encryption).encrypt(lala)
|
|
143
|
+
|
|
144
|
+
return publish(replicationTopic, serialize(decryptedMessage));
|
|
145
|
+
}; */
|
|
146
|
+
|
|
147
|
+
/* export const exchangePeerInfo = async (
|
|
148
|
+
fromId: string,
|
|
149
|
+
replicationTopic: string,
|
|
150
|
+
store: Store<any>,
|
|
151
|
+
heads: string[] | undefined,
|
|
152
|
+
publish: (message: Uint8Array) => Promise<void>,
|
|
153
|
+
sign: (
|
|
154
|
+
bytes: Uint8Array
|
|
155
|
+
) => Promise<{ signature: Uint8Array; publicKey: PublicSignKey }>
|
|
156
|
+
) => {
|
|
157
|
+
const signedMessage = await new MaybeSigned({
|
|
158
|
+
data: serialize(
|
|
159
|
+
new ReplicatorInfo({
|
|
160
|
+
fromId,
|
|
161
|
+
replicationTopic,
|
|
162
|
+
store: store._storeIndex,
|
|
163
|
+
heads,
|
|
164
|
+
})
|
|
165
|
+
),
|
|
166
|
+
}).sign(sign);
|
|
167
|
+
|
|
168
|
+
const decryptedMessage = new DecryptedThing({
|
|
169
|
+
data: serialize(signedMessage),
|
|
170
|
+
}); // TODO add encryption .init(encryption).encrypt(lala)
|
|
171
|
+
|
|
172
|
+
return publish(serialize(decryptedMessage));
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export class ResourceRequirement {
|
|
176
|
+
async ok(_orbitdb: Peerbit): Promise<boolean> {
|
|
177
|
+
throw new Error("Not implemented");
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@variant(0)
|
|
182
|
+
export class NoResourceRequirement extends ResourceRequirement {} */
|
|
183
|
+
|
|
184
|
+
/* @variant(1)
|
|
185
|
+
export class HeapSizeRequirement extends ResourceRequirement {
|
|
186
|
+
|
|
187
|
+
@field({ type: 'u64' })
|
|
188
|
+
heapSize: bigint
|
|
189
|
+
|
|
190
|
+
constructor(properties?: { heapSize: bigint }) {
|
|
191
|
+
super();
|
|
192
|
+
if (properties) {
|
|
193
|
+
this.heapSize = properties.heapSize;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async ok(orbitdb: OrbitDB): Promise<boolean> {
|
|
198
|
+
if (!v8 || typeof orbitdb.heapsizeLimitForForks !== 'number') {
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
const usedHeap: number = v8.getHeapStatistics().used_heap_size;
|
|
202
|
+
return BigInt(usedHeap) + this.heapSize < orbitdb.heapsizeLimitForForks;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
} */
|