@atproto/repo 0.0.1
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 +3 -0
- package/babel.config.js +1 -0
- package/bench/mst.bench.ts +162 -0
- package/bench/repo.bench.ts +39 -0
- package/build.js +22 -0
- package/dist/blockstore/index.d.ts +2 -0
- package/dist/blockstore/ipld-store.d.ts +27 -0
- package/dist/blockstore/memory-blockstore.d.ts +13 -0
- package/dist/cid-set.d.ts +14 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +17731 -0
- package/dist/index.js.map +7 -0
- package/dist/logger.d.ts +2 -0
- package/dist/mst/diff.d.ts +33 -0
- package/dist/mst/index.d.ts +4 -0
- package/dist/mst/mst.d.ts +106 -0
- package/dist/mst/util.d.ts +9 -0
- package/dist/mst/walker.d.ts +22 -0
- package/dist/repo.d.ts +39 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/types.d.ts +12 -0
- package/dist/sync.d.ts +9 -0
- package/dist/types.d.ts +368 -0
- package/dist/util.d.ts +13 -0
- package/dist/verify.d.ts +5 -0
- package/jest.bench.config.js +7 -0
- package/jest.config.js +6 -0
- package/package.json +34 -0
- package/src/blockstore/index.ts +2 -0
- package/src/blockstore/ipld-store.ts +103 -0
- package/src/blockstore/memory-blockstore.ts +49 -0
- package/src/cid-set.ts +50 -0
- package/src/index.ts +7 -0
- package/src/logger.ts +5 -0
- package/src/mst/diff.ts +106 -0
- package/src/mst/index.ts +4 -0
- package/src/mst/mst.ts +796 -0
- package/src/mst/util.ts +122 -0
- package/src/mst/walker.ts +120 -0
- package/src/repo.ts +312 -0
- package/src/storage/index.ts +1 -0
- package/src/storage/types.ts +12 -0
- package/src/sync.ts +38 -0
- package/src/types.ts +101 -0
- package/src/util.ts +88 -0
- package/src/verify.ts +62 -0
- package/tests/_util.ts +254 -0
- package/tests/mst.test.ts +280 -0
- package/tests/repo.test.ts +107 -0
- package/tests/sync.test.ts +129 -0
- package/tsconfig.build.json +4 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +14 -0
- package/update-pkg.js +14 -0
package/README.md
ADDED
package/babel.config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('../../babel.config.js')
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { CID } from 'multiformats'
|
|
2
|
+
import { Fanout, MemoryBlockstore, MST, NodeEntry } from '../src'
|
|
3
|
+
import * as util from '../tests/_util'
|
|
4
|
+
import fs from 'fs'
|
|
5
|
+
|
|
6
|
+
type BenchmarkData = {
|
|
7
|
+
fanout: number
|
|
8
|
+
size: number
|
|
9
|
+
addTime: string
|
|
10
|
+
saveTime: string
|
|
11
|
+
walkTime: string
|
|
12
|
+
depth: number
|
|
13
|
+
maxWidth: number
|
|
14
|
+
blockstoreSize: number
|
|
15
|
+
largestProofSize: number
|
|
16
|
+
avgProofSize: number
|
|
17
|
+
widths: Record<number, number>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('MST Benchmarks', () => {
|
|
21
|
+
let mapping: Record<string, CID>
|
|
22
|
+
let shuffled: [string, CID][]
|
|
23
|
+
|
|
24
|
+
const size = 500000
|
|
25
|
+
|
|
26
|
+
beforeAll(async () => {
|
|
27
|
+
mapping = await util.generateBulkTidMapping(size)
|
|
28
|
+
shuffled = util.shuffle(Object.entries(mapping))
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
// const fanouts: Fanout[] = [8, 16, 32]
|
|
32
|
+
const fanouts: Fanout[] = [16, 32]
|
|
33
|
+
it('benchmarks various fanouts', async () => {
|
|
34
|
+
let benches: BenchmarkData[] = []
|
|
35
|
+
for (const fanout of fanouts) {
|
|
36
|
+
const blockstore = new MemoryBlockstore()
|
|
37
|
+
let mst = await MST.create(blockstore, [], { fanout })
|
|
38
|
+
|
|
39
|
+
const start = Date.now()
|
|
40
|
+
|
|
41
|
+
for (const entry of shuffled) {
|
|
42
|
+
mst = await mst.add(entry[0], entry[1])
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const doneAdding = Date.now()
|
|
46
|
+
|
|
47
|
+
const root = await mst.save()
|
|
48
|
+
|
|
49
|
+
const doneSaving = Date.now()
|
|
50
|
+
|
|
51
|
+
let reloaded = await MST.load(blockstore, root, { fanout })
|
|
52
|
+
const widthTracker = new NodeWidths()
|
|
53
|
+
for await (const entry of reloaded.walk()) {
|
|
54
|
+
await widthTracker.trackEntry(entry)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const doneWalking = Date.now()
|
|
58
|
+
|
|
59
|
+
const paths = await reloaded.paths()
|
|
60
|
+
let largestProof = 0
|
|
61
|
+
let combinedProofSizes = 0
|
|
62
|
+
for (const path of paths) {
|
|
63
|
+
let proofSize = 0
|
|
64
|
+
for (const entry of path) {
|
|
65
|
+
if (entry.isTree()) {
|
|
66
|
+
const bytes = await blockstore.getBytes(entry.pointer)
|
|
67
|
+
proofSize += bytes.byteLength
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
largestProof = Math.max(largestProof, proofSize)
|
|
71
|
+
combinedProofSizes += proofSize
|
|
72
|
+
}
|
|
73
|
+
const avgProofSize = Math.ceil(combinedProofSizes / paths.length)
|
|
74
|
+
|
|
75
|
+
const blockstoreSize = await blockstore.sizeInBytes()
|
|
76
|
+
|
|
77
|
+
benches.push({
|
|
78
|
+
fanout,
|
|
79
|
+
size,
|
|
80
|
+
addTime: secDiff(start, doneAdding),
|
|
81
|
+
saveTime: secDiff(doneAdding, doneSaving),
|
|
82
|
+
walkTime: secDiff(doneSaving, doneWalking),
|
|
83
|
+
depth: await mst.getLayer(),
|
|
84
|
+
blockstoreSize,
|
|
85
|
+
largestProofSize: largestProof,
|
|
86
|
+
avgProofSize: avgProofSize,
|
|
87
|
+
maxWidth: widthTracker.max,
|
|
88
|
+
widths: widthTracker.data,
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
writeBenchData(benches, 'mst-benchmarks')
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
const secDiff = (first: number, second: number): string => {
|
|
96
|
+
return ((second - first) / 1000).toFixed(3)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
class NodeWidths {
|
|
100
|
+
data = {
|
|
101
|
+
0: 0,
|
|
102
|
+
16: 0,
|
|
103
|
+
32: 0,
|
|
104
|
+
48: 0,
|
|
105
|
+
64: 0,
|
|
106
|
+
96: 0,
|
|
107
|
+
128: 0,
|
|
108
|
+
160: 0,
|
|
109
|
+
192: 0,
|
|
110
|
+
224: 0,
|
|
111
|
+
256: 0,
|
|
112
|
+
}
|
|
113
|
+
max = 0
|
|
114
|
+
|
|
115
|
+
async trackEntry(entry: NodeEntry) {
|
|
116
|
+
if (!entry.isTree()) return
|
|
117
|
+
const entries = await entry.getEntries()
|
|
118
|
+
const width = entries.filter((e) => e.isLeaf()).length
|
|
119
|
+
this.max = Math.max(this.max, width)
|
|
120
|
+
if (width >= 0) this.data[0]++
|
|
121
|
+
if (width >= 16) this.data[16]++
|
|
122
|
+
if (width >= 32) this.data[32]++
|
|
123
|
+
if (width >= 48) this.data[48]++
|
|
124
|
+
if (width >= 64) this.data[64]++
|
|
125
|
+
if (width >= 96) this.data[96]++
|
|
126
|
+
if (width >= 128) this.data[128]++
|
|
127
|
+
if (width >= 160) this.data[160]++
|
|
128
|
+
if (width >= 192) this.data[192]++
|
|
129
|
+
if (width >= 224) this.data[224]++
|
|
130
|
+
if (width >= 256) this.data[256]++
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const writeBenchData = (benches: BenchmarkData[], fileLoc: string) => {
|
|
135
|
+
let toWrite = ''
|
|
136
|
+
for (const bench of benches) {
|
|
137
|
+
toWrite += `Fanout: ${bench.fanout}
|
|
138
|
+
----------------------
|
|
139
|
+
Time to add ${bench.size} leaves: ${bench.addTime}s
|
|
140
|
+
Time to save tree with ${bench.size} leaves: ${bench.saveTime}s
|
|
141
|
+
Time to reconstruct & walk ${bench.size} leaves: ${bench.walkTime}s
|
|
142
|
+
Tree depth: ${bench.depth}
|
|
143
|
+
Max Node Width (only counting leaves): ${bench.maxWidth}
|
|
144
|
+
The total blockstore size is: ${bench.blockstoreSize} bytes
|
|
145
|
+
Largest proof size: ${bench.largestProofSize} bytes
|
|
146
|
+
Average proof size: ${bench.avgProofSize} bytes
|
|
147
|
+
Nodes with >= 0 leaves: ${bench.widths[0]}
|
|
148
|
+
Nodes with >= 16 leaves: ${bench.widths[16]}
|
|
149
|
+
Nodes with >= 32 leaves: ${bench.widths[32]}
|
|
150
|
+
Nodes with >= 48 leaves: ${bench.widths[48]}
|
|
151
|
+
Nodes with >= 64 leaves: ${bench.widths[64]}
|
|
152
|
+
Nodes with >= 96 leaves: ${bench.widths[96]}
|
|
153
|
+
Nodes with >= 128 leaves: ${bench.widths[128]}
|
|
154
|
+
Nodes with >= 160 leaves: ${bench.widths[160]}
|
|
155
|
+
Nodes with >= 192 leaves: ${bench.widths[192]}
|
|
156
|
+
Nodes with >= 224 leaves: ${bench.widths[224]}
|
|
157
|
+
Nodes with >= 256 leaves: ${bench.widths[256]}
|
|
158
|
+
|
|
159
|
+
`
|
|
160
|
+
}
|
|
161
|
+
fs.writeFileSync(fileLoc, toWrite)
|
|
162
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as auth from '@atproto/auth'
|
|
2
|
+
import { MemoryBlockstore, Repo } from '../src'
|
|
3
|
+
import * as util from '../tests/_util'
|
|
4
|
+
|
|
5
|
+
describe('Repo Benchmarks', () => {
|
|
6
|
+
const verifier = new auth.Verifier()
|
|
7
|
+
const size = 10000
|
|
8
|
+
|
|
9
|
+
let blockstore: MemoryBlockstore
|
|
10
|
+
let authStore: auth.AuthStore
|
|
11
|
+
let repo: Repo
|
|
12
|
+
|
|
13
|
+
beforeAll(async () => {
|
|
14
|
+
blockstore = new MemoryBlockstore()
|
|
15
|
+
authStore = await verifier.createTempAuthStore()
|
|
16
|
+
await authStore.claimFull()
|
|
17
|
+
repo = await Repo.create(blockstore, await authStore.did(), authStore)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('calculates size', async () => {
|
|
21
|
+
const posts = repo.getCollection('app.bsky.post')
|
|
22
|
+
for (let i = 0; i < size; i++) {
|
|
23
|
+
if (i % 500 === 0) {
|
|
24
|
+
console.log(i)
|
|
25
|
+
}
|
|
26
|
+
await posts.createRecord({
|
|
27
|
+
$type: 'app.bsky.post',
|
|
28
|
+
text: util.randomStr(150),
|
|
29
|
+
reply: {
|
|
30
|
+
root: 'at://did:plc:1234abdefeoi23/app.bsky.post/12345678912345',
|
|
31
|
+
parent: 'at://did:plc:1234abdefeoi23/app.bsky.post/12345678912345',
|
|
32
|
+
},
|
|
33
|
+
createdAt: new Date().toISOString(),
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log('SIZE: ', await blockstore.sizeInBytes())
|
|
38
|
+
})
|
|
39
|
+
})
|
package/build.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const pkgJson = require('@npmcli/package-json')
|
|
2
|
+
const { nodeExternalsPlugin } = require('esbuild-node-externals')
|
|
3
|
+
|
|
4
|
+
const buildShallow =
|
|
5
|
+
process.argv.includes('--shallow') || process.env.ATP_BUILD_SHALLOW === 'true'
|
|
6
|
+
|
|
7
|
+
if (process.argv.includes('--update-main-to-dist')) {
|
|
8
|
+
return pkgJson
|
|
9
|
+
.load(__dirname)
|
|
10
|
+
.then((pkg) => pkg.update({ main: 'dist/index.js' }))
|
|
11
|
+
.then((pkg) => pkg.save())
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
require('esbuild').build({
|
|
15
|
+
logLevel: 'info',
|
|
16
|
+
entryPoints: ['src/index.ts'],
|
|
17
|
+
bundle: true,
|
|
18
|
+
sourcemap: true,
|
|
19
|
+
outdir: 'dist',
|
|
20
|
+
platform: 'node',
|
|
21
|
+
plugins: buildShallow ? [nodeExternalsPlugin()] : [],
|
|
22
|
+
})
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { CID } from 'multiformats/cid';
|
|
2
|
+
import { BlockWriter } from '@ipld/car/writer';
|
|
3
|
+
import { check } from '@atproto/common';
|
|
4
|
+
import { BlockReader } from '@ipld/car/api';
|
|
5
|
+
import CidSet from '../cid-set';
|
|
6
|
+
export declare abstract class IpldStore {
|
|
7
|
+
staged: Map<string, Uint8Array>;
|
|
8
|
+
constructor();
|
|
9
|
+
abstract getSavedBytes(cid: CID): Promise<Uint8Array | null>;
|
|
10
|
+
abstract hasSavedBlock(cid: CID): Promise<boolean>;
|
|
11
|
+
abstract saveStaged(): Promise<void>;
|
|
12
|
+
abstract destroySaved(): Promise<void>;
|
|
13
|
+
stageBytes(k: CID, v: Uint8Array): Promise<void>;
|
|
14
|
+
stage(value: unknown): Promise<CID>;
|
|
15
|
+
getBytes(cid: CID): Promise<Uint8Array>;
|
|
16
|
+
get<T>(cid: CID, schema: check.Def<T>): Promise<T>;
|
|
17
|
+
getUnchecked(cid: CID): Promise<unknown>;
|
|
18
|
+
has(cid: CID): Promise<boolean>;
|
|
19
|
+
isMissing(cid: CID): Promise<boolean>;
|
|
20
|
+
checkMissing(cids: CidSet): Promise<CidSet>;
|
|
21
|
+
clearStaged(): Promise<void>;
|
|
22
|
+
destroy(): Promise<void>;
|
|
23
|
+
addToCar(car: BlockWriter, cid: CID): Promise<void>;
|
|
24
|
+
stageCar(buf: Uint8Array): Promise<CID>;
|
|
25
|
+
stageCarBlocks(car: BlockReader): Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
export default IpldStore;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { CID } from 'multiformats/cid';
|
|
2
|
+
import IpldStore from './ipld-store';
|
|
3
|
+
export declare class MemoryBlockstore extends IpldStore {
|
|
4
|
+
blocks: Map<string, Uint8Array>;
|
|
5
|
+
constructor();
|
|
6
|
+
getSavedBytes(cid: CID): Promise<Uint8Array | null>;
|
|
7
|
+
hasSavedBlock(cid: CID): Promise<boolean>;
|
|
8
|
+
saveStaged(): Promise<void>;
|
|
9
|
+
sizeInBytes(): Promise<number>;
|
|
10
|
+
destroySaved(): Promise<void>;
|
|
11
|
+
getContents(): Promise<Record<string, unknown>>;
|
|
12
|
+
}
|
|
13
|
+
export default MemoryBlockstore;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { CID } from 'multiformats';
|
|
2
|
+
export declare class CidSet {
|
|
3
|
+
private set;
|
|
4
|
+
constructor(arr?: CID[]);
|
|
5
|
+
add(cid: CID): CidSet;
|
|
6
|
+
addSet(toMerge: CidSet): CidSet;
|
|
7
|
+
subtractSet(toSubtract: CidSet): CidSet;
|
|
8
|
+
delete(cid: CID): this;
|
|
9
|
+
has(cid: CID): boolean;
|
|
10
|
+
size(): number;
|
|
11
|
+
clear(): CidSet;
|
|
12
|
+
toList(): CID[];
|
|
13
|
+
}
|
|
14
|
+
export default CidSet;
|