@@ -16,29 +16,63 @@ module Make (Data : Binable.S) = struct
1616
1717 module Rw = Read_write (F )
1818
19- type t = { env : Rw .t ; db : Rw .holder ; counter : int ref }
19+ type t =
20+ { env : Rw .t
21+ ; db : Rw .holder
22+ ; counter : int ref
23+ ; logger : Logger .t
24+ ; garbage : int Hash_set .t
25+ (* * A list of ids that are no longer reachable from OCaml's side *)
26+ }
27+
28+ (* * How big can the above hashset be before we do a cleanup *)
29+ let garbage_size_limit = 512
2030
2131 let initialize path ~logger =
2232 Async.Deferred.Result. map (Disk_cache_utils. initialize_dir path ~logger )
2333 ~f: (fun path ->
2434 let env, db = Rw. create path in
25- { env; db; counter = ref 0 } )
35+ { env
36+ ; db
37+ ; counter = ref 0
38+ ; logger
39+ ; garbage = Hash_set. create (module Int )
40+ } )
2641
2742 type id = { idx : int }
2843
29- let get ({ env; db; _ } : t ) ({ idx } : id ) : Data.t =
44+ let get ({ env; db; logger; _ } : t ) ({ idx } : id ) : Data.t =
45+ [% log debug] " Getting data at %d in LMDB cache" idx
46+ ~metadata: [ (" index" , `Int idx) ] ;
3047 Rw. get ~env db idx |> Option. value_exn
3148
32- let put ({ env; db; counter } : t ) (x : Data.t ) : id =
49+ let put ({ env; db; counter; logger; garbage } : t ) (x : Data.t ) : id =
50+ (* TODO: we may reuse IDs by pulling them from the `garbage` hash set *)
3351 let idx = ! counter in
3452 incr counter ;
3553 let res = { idx } in
3654 (* When this reference is GC'd, delete the file. *)
37- Gc.Expert. add_finalizer_last_exn res (fun () -> Rw. remove ~env db idx) ;
55+ Gc.Expert. add_finalizer_last_exn res (fun () ->
56+ (* The actual deletion is delayed, as GC maybe triggered in LMDB's
57+ critical section. LMDB critical section then will be re-entered if
58+ it's invoked directly in a GC hook.
59+ This causes mutex double-acquiring and node freezes. *)
60+ [% log spam] " Data at %d is GCed, marking as garbage" idx
61+ ~metadata: [ (" index" , `Int idx) ] ;
62+ Hash_set. add garbage idx ) ;
63+ if Hash_set. length garbage > = garbage_size_limit then (
64+ Hash_set. iter garbage ~f: (fun to_remove ->
65+ [% log spam] " Instructing LMDB to remove garbage at index %d" to_remove
66+ ~metadata: [ (" index" , `Int to_remove) ] ;
67+ Rw. remove ~env db to_remove ) ;
68+ Hash_set. clear garbage ) ;
3869 Rw. set ~env db idx x ;
3970 res
4071
41- let iteri ({ env; db; _ } : t ) ~f = Rw. iter ~env db ~f
72+ let iteri ({ env; db; logger; _ } : t ) ~f =
73+ Rw. iter ~env db ~f: (fun k v ->
74+ [% log spam] " Iterating at index %d" k ~metadata: [ (" index" , `Int k) ] ;
75+ f k v )
4276
4377 let count ({ env; db; _ } : t ) =
4478 let sum = ref 0 in
@@ -52,7 +86,7 @@ let%test_module "disk_cache lmdb" =
5286 ( module struct
5387 include Disk_cache_test_lib. Make_extended (Make )
5488
55- let % test_unit " remove data on gc" = remove_data_on_gc ()
89+ let % test_unit " remove data on gc" = remove_data_on_gc ~gc_strict: false ()
5690
5791 let % test_unit " simple read/write (with iteration)" =
5892 simple_write_with_iteration ()
0 commit comments