tangled
alpha
login
or
join now
futur.blue
/
pegasus
57
fork
atom
objective categorical abstract machine language personal data server
57
fork
atom
overview
issues
2
pulls
pipelines
Undo async sequencer
futur.blue
2 months ago
4ea081a0
d765faf6
verified
This commit was signed with the committer's
known signature
.
futur.blue
SSH Key Fingerprint:
SHA256:QHGqHWNpqYyw9bt8KmPuJIyeZX9SZewBZ0PR1COtKQ0=
+190
-219
2 changed files
expand all
collapse all
unified
split
pegasus
lib
repository.ml
sequencer.ml
+186
-215
pegasus/lib/repository.ml
···
8
8
module String_map = Lex.String_map
9
9
module Tid = Mist.Tid
10
10
11
11
-
let write_locks : (string, Lwt_mutex.t) Hashtbl.t = Hashtbl.create 100
12
12
-
13
13
-
let write_lock_mutex = Lwt_mutex.create ()
14
14
-
15
15
-
let with_write_lock did f =
16
16
-
let%lwt lock =
17
17
-
Lwt_mutex.with_lock write_lock_mutex (fun () ->
18
18
-
match Hashtbl.find_opt write_locks did with
19
19
-
| Some l ->
20
20
-
Lwt.return l
21
21
-
| None ->
22
22
-
let l = Lwt_mutex.create () in
23
23
-
Hashtbl.add write_locks did l ;
24
24
-
Lwt.return l )
25
25
-
in
26
26
-
Lwt_mutex.with_lock lock f
27
27
-
28
11
module Write_op = struct
29
12
let create = "com.atproto.repo.applyWrites#create"
30
13
···
261
244
262
245
let apply_writes (t : t) (writes : repo_write list) (swap_commit : Cid.t option)
263
246
: write_result Lwt.t =
264
264
-
with_write_lock t.did (fun () ->
265
265
-
let open Sequencer.Types in
266
266
-
let%lwt prev_commit =
267
267
-
match%lwt User_store.get_commit t.db with
268
268
-
| Some (_, commit) ->
269
269
-
Lwt.return commit
270
270
-
| None ->
271
271
-
failwith ("failed to retrieve commit for " ^ t.did)
272
272
-
in
273
273
-
if swap_commit <> None && swap_commit <> Option.map fst t.commit then
274
274
-
Errors.invalid_request ~name:"InvalidSwap"
275
275
-
(Format.sprintf "swapCommit cid %s did not match last commit cid %s"
276
276
-
(Cid.to_string (Option.get swap_commit))
277
277
-
( match t.commit with
278
278
-
| Some (c, _) ->
279
279
-
Cid.to_string c
280
280
-
| None ->
281
281
-
"null" ) ) ;
282
282
-
let cached_store = Cached_store.create t.db in
283
283
-
let mst : Cached_mst.t ref =
284
284
-
ref (Cached_mst.create cached_store prev_commit.data)
285
285
-
in
286
286
-
t.block_map <- None ;
287
287
-
(* ops to emit, built in loop because prev_data (previous cid) is otherwise inaccessible *)
288
288
-
let commit_ops : commit_evt_op list ref = ref [] in
289
289
-
let added_leaves = ref Block_map.empty in
290
290
-
let%lwt results =
291
291
-
Lwt_list.map_s
292
292
-
(fun (w : repo_write) ->
293
293
-
match w with
294
294
-
| Create {collection; rkey; value; _} ->
295
295
-
let rkey = Option.value rkey ~default:(Tid.now ()) in
296
296
-
let path = Format.sprintf "%s/%s" collection rkey in
297
297
-
let uri = Format.sprintf "at://%s/%s" t.did path in
298
298
-
let%lwt () =
299
299
-
match%lwt User_store.get_record_cid t.db path with
300
300
-
| Some cid ->
301
301
-
Errors.invalid_request ~name:"InvalidSwap"
302
302
-
(Format.sprintf
303
303
-
"attempted to write record %s that already exists \
304
304
-
with cid %s"
305
305
-
path (Cid.to_string cid) )
306
306
-
| None ->
307
307
-
Lwt.return ()
308
308
-
in
309
309
-
let record_with_type : Lex.repo_record =
310
310
-
if String_map.mem "$type" value then value
311
311
-
else String_map.add "$type" (`String collection) value
312
312
-
in
313
313
-
let%lwt cid, block =
314
314
-
User_store.put_record t.db (`LexMap record_with_type) path
315
315
-
in
316
316
-
added_leaves := Block_map.set cid block !added_leaves ;
317
317
-
commit_ops :=
318
318
-
!commit_ops
319
319
-
@ [{action= `Create; path; cid= Some cid; prev= None}] ;
320
320
-
let%lwt new_mst = Cached_mst.add !mst path cid in
321
321
-
mst := new_mst ;
322
322
-
let refs =
323
323
-
Util.find_blob_refs value
324
324
-
|> List.map (fun (r : Mist.Blob_ref.t) -> r.ref)
325
325
-
in
326
326
-
let%lwt () =
327
327
-
match%lwt User_store.put_blob_refs t.db path refs with
328
328
-
| Ok () ->
329
329
-
Lwt.return ()
330
330
-
| Error err ->
331
331
-
raise err
332
332
-
in
333
333
-
Lwt.return
334
334
-
(Create
335
335
-
{ type'= "com.atproto.repo.applyWrites#createResult"
336
336
-
; uri
337
337
-
; cid } )
338
338
-
| Update {collection; rkey; value; swap_record; _} ->
339
339
-
let path = Format.sprintf "%s/%s" collection rkey in
340
340
-
let uri = Format.sprintf "at://%s/%s" t.did path in
341
341
-
let%lwt old_cid = User_store.get_record_cid t.db path in
342
342
-
( if
343
343
-
(swap_record <> None && swap_record <> old_cid)
344
344
-
|| (swap_record = None && old_cid = None)
345
345
-
then
346
346
-
let cid_str =
347
347
-
match old_cid with
348
348
-
| Some cid ->
349
349
-
Cid.to_string cid
350
350
-
| None ->
351
351
-
"null"
352
352
-
in
353
353
-
Errors.invalid_request ~name:"InvalidSwap"
354
354
-
(Format.sprintf
355
355
-
"attempted to update record %s with cid %s" path
356
356
-
cid_str ) ) ;
357
357
-
let%lwt () =
247
247
+
let open Sequencer.Types in
248
248
+
let%lwt prev_commit =
249
249
+
match%lwt User_store.get_commit t.db with
250
250
+
| Some (_, commit) ->
251
251
+
Lwt.return commit
252
252
+
| None ->
253
253
+
failwith ("failed to retrieve commit for " ^ t.did)
254
254
+
in
255
255
+
if swap_commit <> None && swap_commit <> Option.map fst t.commit then
256
256
+
Errors.invalid_request ~name:"InvalidSwap"
257
257
+
(Format.sprintf "swapCommit cid %s did not match last commit cid %s"
258
258
+
(Cid.to_string (Option.get swap_commit))
259
259
+
(match t.commit with Some (c, _) -> Cid.to_string c | None -> "null") ) ;
260
260
+
let cached_store = Cached_store.create t.db in
261
261
+
let mst : Cached_mst.t ref =
262
262
+
ref (Cached_mst.create cached_store prev_commit.data)
263
263
+
in
264
264
+
t.block_map <- None ;
265
265
+
(* ops to emit, built in loop because prev_data (previous cid) is otherwise inaccessible *)
266
266
+
let commit_ops : commit_evt_op list ref = ref [] in
267
267
+
let added_leaves = ref Block_map.empty in
268
268
+
let%lwt results =
269
269
+
Lwt_list.map_s
270
270
+
(fun (w : repo_write) ->
271
271
+
match w with
272
272
+
| Create {collection; rkey; value; _} ->
273
273
+
let rkey = Option.value rkey ~default:(Tid.now ()) in
274
274
+
let path = Format.sprintf "%s/%s" collection rkey in
275
275
+
let uri = Format.sprintf "at://%s/%s" t.did path in
276
276
+
let%lwt () =
277
277
+
match%lwt User_store.get_record_cid t.db path with
278
278
+
| Some cid ->
279
279
+
Errors.invalid_request ~name:"InvalidSwap"
280
280
+
(Format.sprintf
281
281
+
"attempted to write record %s that already exists \
282
282
+
with cid %s"
283
283
+
path (Cid.to_string cid) )
284
284
+
| None ->
285
285
+
Lwt.return ()
286
286
+
in
287
287
+
let record_with_type : Lex.repo_record =
288
288
+
if String_map.mem "$type" value then value
289
289
+
else String_map.add "$type" (`String collection) value
290
290
+
in
291
291
+
let%lwt cid, block =
292
292
+
User_store.put_record t.db (`LexMap record_with_type) path
293
293
+
in
294
294
+
added_leaves := Block_map.set cid block !added_leaves ;
295
295
+
commit_ops :=
296
296
+
!commit_ops @ [{action= `Create; path; cid= Some cid; prev= None}] ;
297
297
+
let%lwt new_mst = Cached_mst.add !mst path cid in
298
298
+
mst := new_mst ;
299
299
+
let refs =
300
300
+
Util.find_blob_refs value
301
301
+
|> List.map (fun (r : Mist.Blob_ref.t) -> r.ref)
302
302
+
in
303
303
+
let%lwt () =
304
304
+
match%lwt User_store.put_blob_refs t.db path refs with
305
305
+
| Ok () ->
306
306
+
Lwt.return ()
307
307
+
| Error err ->
308
308
+
raise err
309
309
+
in
310
310
+
Lwt.return
311
311
+
(Create
312
312
+
{ type'= "com.atproto.repo.applyWrites#createResult"
313
313
+
; uri
314
314
+
; cid } )
315
315
+
| Update {collection; rkey; value; swap_record; _} ->
316
316
+
let path = Format.sprintf "%s/%s" collection rkey in
317
317
+
let uri = Format.sprintf "at://%s/%s" t.did path in
318
318
+
let%lwt old_cid = User_store.get_record_cid t.db path in
319
319
+
( if
320
320
+
(swap_record <> None && swap_record <> old_cid)
321
321
+
|| (swap_record = None && old_cid = None)
322
322
+
then
323
323
+
let cid_str =
358
324
match old_cid with
359
359
-
| Some _ -> (
360
360
-
match%lwt User_store.get_record t.db path with
361
361
-
| Some record ->
362
362
-
let refs =
363
363
-
Util.find_blob_refs record.value
364
364
-
|> List.map (fun (r : Mist.Blob_ref.t) -> r.ref)
365
365
-
in
366
366
-
if not (List.is_empty refs) then
367
367
-
let%lwt _ =
368
368
-
User_store.delete_orphaned_blobs_by_record_path t.db
369
369
-
path
370
370
-
in
371
371
-
Lwt.return_unit
372
372
-
else Lwt.return_unit
373
373
-
| None ->
374
374
-
Lwt.return_unit )
325
325
+
| Some cid ->
326
326
+
Cid.to_string cid
375
327
| None ->
376
376
-
Lwt.return_unit
377
377
-
in
378
378
-
let record_with_type : Lex.repo_record =
379
379
-
if String_map.mem "$type" value then value
380
380
-
else String_map.add "$type" (`String collection) value
381
381
-
in
382
382
-
let%lwt new_cid, new_block =
383
383
-
User_store.put_record t.db (`LexMap record_with_type) path
384
384
-
in
385
385
-
added_leaves := Block_map.set new_cid new_block !added_leaves ;
386
386
-
commit_ops :=
387
387
-
!commit_ops
388
388
-
@ [{action= `Update; path; cid= Some new_cid; prev= old_cid}] ;
389
389
-
let%lwt new_mst = Cached_mst.add !mst path new_cid in
390
390
-
mst := new_mst ;
391
391
-
let refs =
392
392
-
Util.find_blob_refs value
393
393
-
|> List.map (fun (r : Mist.Blob_ref.t) -> r.ref)
394
394
-
in
395
395
-
let%lwt () =
396
396
-
match%lwt User_store.put_blob_refs t.db path refs with
397
397
-
| Ok () ->
398
398
-
Lwt.return ()
399
399
-
| Error err ->
400
400
-
raise err
328
328
+
"null"
401
329
in
402
402
-
Lwt.return
403
403
-
(Update
404
404
-
{ type'= "com.atproto.repo.applyWrites#updateResult"
405
405
-
; uri
406
406
-
; cid= new_cid } )
407
407
-
| Delete {collection; rkey; swap_record; _} ->
408
408
-
let path = Format.sprintf "%s/%s" collection rkey in
409
409
-
let%lwt cid = User_store.get_record_cid t.db path in
410
410
-
( if cid = None || (swap_record <> None && swap_record <> cid)
411
411
-
then
412
412
-
let cid_str =
413
413
-
match cid with
414
414
-
| Some cid ->
415
415
-
Cid.to_string cid
416
416
-
| None ->
417
417
-
"null"
418
418
-
in
419
419
-
Errors.invalid_request ~name:"InvalidSwap"
420
420
-
(Format.sprintf
421
421
-
"attempted to delete record %s with cid %s" path
422
422
-
cid_str ) ) ;
423
423
-
let%lwt () =
330
330
+
Errors.invalid_request ~name:"InvalidSwap"
331
331
+
(Format.sprintf "attempted to update record %s with cid %s"
332
332
+
path cid_str ) ) ;
333
333
+
let%lwt () =
334
334
+
match old_cid with
335
335
+
| Some _ -> (
424
336
match%lwt User_store.get_record t.db path with
425
337
| Some record ->
426
338
let refs =
···
435
347
Lwt.return_unit
436
348
else Lwt.return_unit
437
349
| None ->
438
438
-
Lwt.return_unit
350
350
+
Lwt.return_unit )
351
351
+
| None ->
352
352
+
Lwt.return_unit
353
353
+
in
354
354
+
let record_with_type : Lex.repo_record =
355
355
+
if String_map.mem "$type" value then value
356
356
+
else String_map.add "$type" (`String collection) value
357
357
+
in
358
358
+
let%lwt new_cid, new_block =
359
359
+
User_store.put_record t.db (`LexMap record_with_type) path
360
360
+
in
361
361
+
added_leaves := Block_map.set new_cid new_block !added_leaves ;
362
362
+
commit_ops :=
363
363
+
!commit_ops
364
364
+
@ [{action= `Update; path; cid= Some new_cid; prev= old_cid}] ;
365
365
+
let%lwt new_mst = Cached_mst.add !mst path new_cid in
366
366
+
mst := new_mst ;
367
367
+
let refs =
368
368
+
Util.find_blob_refs value
369
369
+
|> List.map (fun (r : Mist.Blob_ref.t) -> r.ref)
370
370
+
in
371
371
+
let%lwt () =
372
372
+
match%lwt User_store.put_blob_refs t.db path refs with
373
373
+
| Ok () ->
374
374
+
Lwt.return ()
375
375
+
| Error err ->
376
376
+
raise err
377
377
+
in
378
378
+
Lwt.return
379
379
+
(Update
380
380
+
{ type'= "com.atproto.repo.applyWrites#updateResult"
381
381
+
; uri
382
382
+
; cid= new_cid } )
383
383
+
| Delete {collection; rkey; swap_record; _} ->
384
384
+
let path = Format.sprintf "%s/%s" collection rkey in
385
385
+
let%lwt cid = User_store.get_record_cid t.db path in
386
386
+
( if cid = None || (swap_record <> None && swap_record <> cid) then
387
387
+
let cid_str =
388
388
+
match cid with
389
389
+
| Some cid ->
390
390
+
Cid.to_string cid
391
391
+
| None ->
392
392
+
"null"
439
393
in
440
440
-
let%lwt () = User_store.delete_record t.db path in
441
441
-
commit_ops :=
442
442
-
!commit_ops @ [{action= `Delete; path; cid= None; prev= cid}] ;
443
443
-
let%lwt new_mst = Cached_mst.delete !mst path in
444
444
-
mst := new_mst ;
445
445
-
Lwt.return
446
446
-
(Delete {type'= "com.atproto.repo.applyWrites#deleteResult"}) )
447
447
-
writes
448
448
-
in
449
449
-
let new_mst = !mst in
450
450
-
let%lwt new_commit =
451
451
-
put_commit t new_mst.root ~previous:(Some prev_commit)
452
452
-
in
453
453
-
let new_commit_cid, new_commit_signed = new_commit in
454
454
-
let commit_block =
455
455
-
new_commit_signed |> signed_commit_to_yojson |> Dag_cbor.encode_yojson
456
456
-
in
457
457
-
let%lwt proof_blocks =
458
458
-
Lwt_list.fold_left_s
459
459
-
(fun acc ({path; _} : commit_evt_op) ->
460
460
-
let%lwt key_proof =
461
461
-
Cached_mst.proof_for_key new_mst new_mst.root path
394
394
+
Errors.invalid_request ~name:"InvalidSwap"
395
395
+
(Format.sprintf "attempted to delete record %s with cid %s"
396
396
+
path cid_str ) ) ;
397
397
+
let%lwt () =
398
398
+
match%lwt User_store.get_record t.db path with
399
399
+
| Some record ->
400
400
+
let refs =
401
401
+
Util.find_blob_refs record.value
402
402
+
|> List.map (fun (r : Mist.Blob_ref.t) -> r.ref)
403
403
+
in
404
404
+
if not (List.is_empty refs) then
405
405
+
let%lwt _ =
406
406
+
User_store.delete_orphaned_blobs_by_record_path t.db path
407
407
+
in
408
408
+
Lwt.return_unit
409
409
+
else Lwt.return_unit
410
410
+
| None ->
411
411
+
Lwt.return_unit
462
412
in
463
463
-
Lwt.return (Block_map.merge acc key_proof) )
464
464
-
Block_map.empty !commit_ops
465
465
-
in
466
466
-
let proof_blocks = Block_map.merge proof_blocks !added_leaves in
467
467
-
let block_stream =
468
468
-
proof_blocks |> Block_map.entries |> Lwt_seq.of_list
469
469
-
|> Lwt_seq.cons (new_commit_cid, commit_block)
470
470
-
in
471
471
-
let%lwt blocks =
472
472
-
Car.blocks_to_stream new_commit_cid block_stream |> Car.collect_stream
473
473
-
in
474
474
-
let%lwt ds = Data_store.connect () in
475
475
-
let%lwt _ =
476
476
-
Sequencer.sequence_commit ds ~did:t.did ~commit:new_commit_cid
477
477
-
~rev:new_commit_signed.rev ~blocks ~ops:!commit_ops
478
478
-
~since:prev_commit.rev ~prev_data:prev_commit.data ()
479
479
-
in
480
480
-
Lwt.return {commit= new_commit; results} )
413
413
+
let%lwt () = User_store.delete_record t.db path in
414
414
+
commit_ops :=
415
415
+
!commit_ops @ [{action= `Delete; path; cid= None; prev= cid}] ;
416
416
+
let%lwt new_mst = Cached_mst.delete !mst path in
417
417
+
mst := new_mst ;
418
418
+
Lwt.return
419
419
+
(Delete {type'= "com.atproto.repo.applyWrites#deleteResult"}) )
420
420
+
writes
421
421
+
in
422
422
+
let new_mst = !mst in
423
423
+
let%lwt new_commit = put_commit t new_mst.root ~previous:(Some prev_commit) in
424
424
+
let new_commit_cid, new_commit_signed = new_commit in
425
425
+
let commit_block =
426
426
+
new_commit_signed |> signed_commit_to_yojson |> Dag_cbor.encode_yojson
427
427
+
in
428
428
+
let%lwt proof_blocks =
429
429
+
Lwt_list.fold_left_s
430
430
+
(fun acc ({path; _} : commit_evt_op) ->
431
431
+
let%lwt key_proof =
432
432
+
Cached_mst.proof_for_key new_mst new_mst.root path
433
433
+
in
434
434
+
Lwt.return (Block_map.merge acc key_proof) )
435
435
+
Block_map.empty !commit_ops
436
436
+
in
437
437
+
let proof_blocks = Block_map.merge proof_blocks !added_leaves in
438
438
+
let block_stream =
439
439
+
proof_blocks |> Block_map.entries |> Lwt_seq.of_list
440
440
+
|> Lwt_seq.cons (new_commit_cid, commit_block)
441
441
+
in
442
442
+
let%lwt blocks =
443
443
+
Car.blocks_to_stream new_commit_cid block_stream |> Car.collect_stream
444
444
+
in
445
445
+
let%lwt ds = Data_store.connect () in
446
446
+
let%lwt _ =
447
447
+
Sequencer.sequence_commit ds ~did:t.did ~commit:new_commit_cid
448
448
+
~rev:new_commit_signed.rev ~blocks ~ops:!commit_ops
449
449
+
~since:prev_commit.rev ~prev_data:prev_commit.data ()
450
450
+
in
451
451
+
Lwt.return {commit= new_commit; results}
481
452
482
453
let load ?write ?create ?(ensure_active = false) ?ds did : t Lwt.t =
483
454
let%lwt data_store_conn =
+4
-4
pegasus/lib/sequencer.ml
···
743
743
let raw = Dag_cbor.encode_yojson @@ Encode.format_commit evt in
744
744
let%lwt seq = DB.append_event conn ~t:`Commit ~time:time_ms ~data:raw in
745
745
let frame = Frame.encode_message ~seq ~time:time_iso (Commit evt) in
746
746
-
Lwt.async (fun () -> Bus.publish {seq; bytes= frame}) ;
746
746
+
let%lwt () = Bus.publish {seq; bytes= frame} in
747
747
Lwt.return seq
748
748
749
749
let sequence_sync (conn : Data_store.t) ~(did : string) ~(rev : string)
···
754
754
let raw = Dag_cbor.encode_yojson @@ Encode.format_sync evt in
755
755
let%lwt seq = DB.append_event conn ~t:`Sync ~time:time_ms ~data:raw in
756
756
let frame = Frame.encode_message ~seq ~time:time_iso (Sync evt) in
757
757
-
Lwt.async (fun () -> Bus.publish {seq; bytes= frame}) ;
757
757
+
let%lwt () = Bus.publish {seq; bytes= frame} in
758
758
Lwt.return seq
759
759
760
760
let sequence_identity (conn : Data_store.t) ~(did : string)
···
765
765
let raw = Dag_cbor.encode_yojson @@ Encode.format_identity evt in
766
766
let%lwt seq = DB.append_event conn ~t:`Identity ~time:time_ms ~data:raw in
767
767
let frame = Frame.encode_message ~seq ~time:time_iso (Identity evt) in
768
768
-
Lwt.async (fun () -> Bus.publish {seq; bytes= frame}) ;
768
768
+
let%lwt () = Bus.publish {seq; bytes= frame} in
769
769
Lwt.return seq
770
770
771
771
let sequence_account (conn : Data_store.t) ~(did : string) ~(active : bool)
···
776
776
let raw = Dag_cbor.encode_yojson @@ Encode.format_account evt in
777
777
let%lwt seq = DB.append_event conn ~t:`Account ~time:time_ms ~data:raw in
778
778
let frame = Frame.encode_message ~seq ~time:time_iso (Account evt) in
779
779
-
Lwt.async (fun () -> Bus.publish {seq; bytes= frame}) ;
779
779
+
let%lwt () = Bus.publish {seq; bytes= frame} in
780
780
Lwt.return seq