2017-02-24 20:17:53 +04:00
|
|
|
(**************************************************************************)
|
|
|
|
(* *)
|
|
|
|
(* Copyright (c) 2014 - 2016. *)
|
|
|
|
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
|
|
|
(* *)
|
|
|
|
(* All rights reserved. No warranty, explicit or implicit, provided. *)
|
|
|
|
(* *)
|
|
|
|
(**************************************************************************)
|
|
|
|
|
2017-04-19 23:46:10 +04:00
|
|
|
module type DISTRIBUTED_DB = sig
|
2017-03-28 15:31:41 +04:00
|
|
|
|
2017-02-24 20:17:53 +04:00
|
|
|
type t
|
|
|
|
type key
|
|
|
|
type value
|
2017-03-28 15:31:41 +04:00
|
|
|
type param
|
|
|
|
|
2017-02-24 20:17:53 +04:00
|
|
|
val known: t -> key -> bool Lwt.t
|
2017-04-19 23:46:10 +04:00
|
|
|
|
|
|
|
type error += Missing_data of key
|
|
|
|
val read: t -> key -> value tzresult Lwt.t
|
|
|
|
val read_opt: t -> key -> value option Lwt.t
|
2017-02-24 20:17:53 +04:00
|
|
|
val read_exn: t -> key -> value Lwt.t
|
2017-03-28 15:31:41 +04:00
|
|
|
|
|
|
|
val prefetch: t -> ?peer:P2p.Peer_id.t -> key -> param -> unit
|
|
|
|
val fetch: t -> ?peer:P2p.Peer_id.t -> key -> param -> value Lwt.t
|
|
|
|
|
2017-11-06 18:23:06 +04:00
|
|
|
val clear_or_cancel: t -> key -> unit
|
2017-02-24 20:17:53 +04:00
|
|
|
val inject: t -> key -> value -> bool Lwt.t
|
|
|
|
val watch: t -> (key * value) Lwt_stream.t * Watcher.stopper
|
2017-03-28 15:31:41 +04:00
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
module type DISK_TABLE = sig
|
|
|
|
type store
|
|
|
|
type key
|
|
|
|
type value
|
|
|
|
val known: store -> key -> bool Lwt.t
|
|
|
|
val read: store -> key -> value tzresult Lwt.t
|
|
|
|
val read_opt: store -> key -> value option Lwt.t
|
|
|
|
val read_exn: store -> key -> value Lwt.t
|
|
|
|
end
|
|
|
|
|
|
|
|
module type MEMORY_TABLE = sig
|
|
|
|
type 'a t
|
|
|
|
type key
|
|
|
|
val create: int -> 'a t
|
|
|
|
val find: 'a t -> key -> 'a
|
|
|
|
val add: 'a t -> key -> 'a -> unit
|
|
|
|
val replace: 'a t -> key -> 'a -> unit
|
|
|
|
val remove: 'a t -> key -> unit
|
|
|
|
val fold: (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
|
2017-02-24 20:17:53 +04:00
|
|
|
end
|
|
|
|
|
|
|
|
module type SCHEDULER_EVENTS = sig
|
|
|
|
type t
|
|
|
|
type key
|
|
|
|
val request: t -> P2p.Peer_id.t option -> key -> unit
|
|
|
|
val notify: t -> P2p.Peer_id.t -> key -> unit
|
2017-11-06 18:23:06 +04:00
|
|
|
val notify_cancelation: t -> key -> unit
|
2017-02-24 20:17:53 +04:00
|
|
|
val notify_unrequested: t -> P2p.Peer_id.t -> key -> unit
|
|
|
|
val notify_duplicate: t -> P2p.Peer_id.t -> key -> unit
|
2017-03-28 15:31:41 +04:00
|
|
|
val notify_invalid: t -> P2p.Peer_id.t -> key -> unit
|
2017-02-24 20:17:53 +04:00
|
|
|
end
|
|
|
|
|
2017-03-28 15:31:41 +04:00
|
|
|
module type PRECHECK = sig
|
|
|
|
type key
|
|
|
|
type param
|
2017-04-19 23:46:10 +04:00
|
|
|
type notified_value
|
2017-03-28 15:31:41 +04:00
|
|
|
type value
|
2017-04-19 23:46:10 +04:00
|
|
|
val precheck: key -> param -> notified_value -> value option
|
2017-03-28 15:31:41 +04:00
|
|
|
end
|
2017-02-24 20:17:53 +04:00
|
|
|
|
2017-03-28 15:31:41 +04:00
|
|
|
module Make_table
|
2017-07-21 18:16:39 +04:00
|
|
|
(Hash : sig
|
|
|
|
type t
|
|
|
|
val name : string
|
|
|
|
val encoding : t Data_encoding.t
|
|
|
|
val pp : Format.formatter -> t -> unit
|
|
|
|
end)
|
2017-03-28 15:31:41 +04:00
|
|
|
(Disk_table : DISK_TABLE with type key := Hash.t)
|
|
|
|
(Memory_table : MEMORY_TABLE with type key := Hash.t)
|
|
|
|
(Scheduler : SCHEDULER_EVENTS with type key := Hash.t)
|
|
|
|
(Precheck : PRECHECK with type key := Hash.t
|
|
|
|
and type value := Disk_table.value) : sig
|
|
|
|
|
2017-04-19 23:46:10 +04:00
|
|
|
include DISTRIBUTED_DB with type key = Hash.t
|
|
|
|
and type value = Disk_table.value
|
|
|
|
and type param = Precheck.param
|
2017-02-24 20:17:53 +04:00
|
|
|
val create:
|
|
|
|
?global_input:(key * value) Watcher.input ->
|
|
|
|
Scheduler.t -> Disk_table.store -> t
|
2017-04-19 23:46:10 +04:00
|
|
|
val notify: t -> P2p.Peer_id.t -> key -> Precheck.notified_value -> unit Lwt.t
|
2017-02-24 20:17:53 +04:00
|
|
|
|
|
|
|
end = struct
|
|
|
|
|
|
|
|
type key = Hash.t
|
|
|
|
type value = Disk_table.value
|
2017-03-28 15:31:41 +04:00
|
|
|
type param = Precheck.param
|
2017-02-24 20:17:53 +04:00
|
|
|
|
|
|
|
type t = {
|
|
|
|
scheduler: Scheduler.t ;
|
|
|
|
disk: Disk_table.store ;
|
|
|
|
memory: status Memory_table.t ;
|
|
|
|
global_input: (key * value) Watcher.input option ;
|
|
|
|
input: (key * value) Watcher.input ;
|
|
|
|
}
|
|
|
|
|
|
|
|
and status =
|
2017-11-08 14:47:08 +04:00
|
|
|
| Pending of { wakener : value Lwt.u ;
|
|
|
|
param : param }
|
2017-02-24 20:17:53 +04:00
|
|
|
| Found of value
|
|
|
|
|
|
|
|
let known s k =
|
|
|
|
match Memory_table.find s.memory k with
|
|
|
|
| exception Not_found -> Disk_table.known s.disk k
|
|
|
|
| Pending _ -> Lwt.return_false
|
|
|
|
| Found _ -> Lwt.return_true
|
|
|
|
|
2017-04-19 23:46:10 +04:00
|
|
|
let read_opt s k =
|
2017-02-24 20:17:53 +04:00
|
|
|
match Memory_table.find s.memory k with
|
|
|
|
| exception Not_found -> Disk_table.read_opt s.disk k
|
|
|
|
| Found v -> Lwt.return (Some v)
|
|
|
|
| Pending _ -> Lwt.return_none
|
|
|
|
|
|
|
|
let read_exn s k =
|
|
|
|
match Memory_table.find s.memory k with
|
|
|
|
| exception Not_found -> Disk_table.read_exn s.disk k
|
|
|
|
| Found v -> Lwt.return v
|
|
|
|
| Pending _ -> Lwt.fail Not_found
|
|
|
|
|
2017-04-19 23:46:10 +04:00
|
|
|
type error += Missing_data of key
|
|
|
|
|
2017-07-21 18:16:39 +04:00
|
|
|
let () =
|
|
|
|
Error_monad.register_error_kind `Permanent
|
|
|
|
~id: ("distributed_db." ^ Hash.name ^ ".missing")
|
|
|
|
~title: ("Missing " ^ Hash.name)
|
|
|
|
~description: ("Some " ^ Hash.name ^ " is missing from the distributed db")
|
|
|
|
~pp: (fun ppf key ->
|
|
|
|
Format.fprintf ppf "Missing %s %a" Hash.name Hash.pp key)
|
|
|
|
(Data_encoding.obj1 (Data_encoding.req "key" Hash.encoding))
|
|
|
|
(function Missing_data key -> Some key | _ -> None)
|
|
|
|
(fun key -> Missing_data key)
|
|
|
|
|
2017-04-19 23:46:10 +04:00
|
|
|
let read s k =
|
|
|
|
match Memory_table.find s.memory k with
|
|
|
|
| exception Not_found ->
|
|
|
|
trace (Missing_data k) @@
|
|
|
|
Disk_table.read s.disk k
|
|
|
|
| Found v -> return v
|
|
|
|
| Pending _ -> fail (Missing_data k)
|
|
|
|
|
2017-03-28 15:31:41 +04:00
|
|
|
let fetch s ?peer k param =
|
2017-02-24 20:17:53 +04:00
|
|
|
match Memory_table.find s.memory k with
|
|
|
|
| exception Not_found -> begin
|
|
|
|
Disk_table.read_opt s.disk k >>= function
|
|
|
|
| Some v -> Lwt.return v
|
2017-09-29 20:43:13 +04:00
|
|
|
| None ->
|
|
|
|
match Memory_table.find s.memory k with
|
|
|
|
| exception Not_found -> begin
|
|
|
|
let waiter, wakener = Lwt.wait () in
|
2017-11-08 14:47:08 +04:00
|
|
|
Memory_table.add s.memory k (Pending { wakener ; param }) ;
|
2017-09-29 20:43:13 +04:00
|
|
|
Scheduler.request s.scheduler peer k ;
|
|
|
|
waiter
|
|
|
|
end
|
2017-11-08 14:47:08 +04:00
|
|
|
| Pending { wakener = w ; _ } ->
|
2017-09-29 20:43:13 +04:00
|
|
|
Scheduler.request s.scheduler peer k ;
|
|
|
|
Lwt.waiter_of_wakener w
|
|
|
|
| Found v -> Lwt.return v
|
2017-02-24 20:17:53 +04:00
|
|
|
end
|
2017-11-08 14:47:08 +04:00
|
|
|
| Pending { wakener = w ; _ } ->
|
2017-09-29 20:43:13 +04:00
|
|
|
Scheduler.request s.scheduler peer k ;
|
|
|
|
Lwt.waiter_of_wakener w
|
2017-02-24 20:17:53 +04:00
|
|
|
| Found v -> Lwt.return v
|
|
|
|
|
2017-03-28 15:31:41 +04:00
|
|
|
let prefetch s ?peer k param = Lwt.ignore_result (fetch s ?peer k param)
|
2017-02-24 20:17:53 +04:00
|
|
|
|
|
|
|
let notify s p k v =
|
|
|
|
match Memory_table.find s.memory k with
|
|
|
|
| exception Not_found -> begin
|
|
|
|
Disk_table.known s.disk k >>= function
|
|
|
|
| true ->
|
|
|
|
Scheduler.notify_duplicate s.scheduler p k ;
|
|
|
|
Lwt.return_unit
|
|
|
|
| false ->
|
|
|
|
Scheduler.notify_unrequested s.scheduler p k ;
|
|
|
|
Lwt.return_unit
|
|
|
|
end
|
2017-11-08 14:47:08 +04:00
|
|
|
| Pending { wakener = w ; param } -> begin
|
2017-04-19 23:46:10 +04:00
|
|
|
match Precheck.precheck k param v with
|
|
|
|
| None ->
|
|
|
|
Scheduler.notify_invalid s.scheduler p k ;
|
|
|
|
Lwt.return_unit
|
|
|
|
| Some v ->
|
|
|
|
Scheduler.notify s.scheduler p k ;
|
|
|
|
Memory_table.replace s.memory k (Found v) ;
|
|
|
|
Lwt.wakeup w v ;
|
|
|
|
iter_option s.global_input
|
|
|
|
~f:(fun input -> Watcher.notify input (k, v)) ;
|
|
|
|
Watcher.notify s.input (k, v) ;
|
|
|
|
Lwt.return_unit
|
2017-03-28 15:31:41 +04:00
|
|
|
end
|
2017-02-24 20:17:53 +04:00
|
|
|
| Found _ ->
|
|
|
|
Scheduler.notify_duplicate s.scheduler p k ;
|
|
|
|
Lwt.return_unit
|
|
|
|
|
|
|
|
let inject s k v =
|
|
|
|
match Memory_table.find s.memory k with
|
|
|
|
| exception Not_found -> begin
|
|
|
|
Disk_table.known s.disk k >>= function
|
|
|
|
| true ->
|
|
|
|
Lwt.return_false
|
|
|
|
| false ->
|
|
|
|
Memory_table.add s.memory k (Found v) ;
|
|
|
|
Lwt.return_true
|
|
|
|
end
|
|
|
|
| Pending _
|
|
|
|
| Found _ ->
|
|
|
|
Lwt.return_false
|
|
|
|
|
2017-11-06 18:23:06 +04:00
|
|
|
let clear_or_cancel s k =
|
2017-02-24 20:17:53 +04:00
|
|
|
match Memory_table.find s.memory k with
|
2017-06-09 19:54:08 +04:00
|
|
|
| exception Not_found -> ()
|
2017-11-08 14:47:08 +04:00
|
|
|
| Pending { wakener = w ; _ } ->
|
2017-11-06 18:23:06 +04:00
|
|
|
Scheduler.notify_cancelation s.scheduler k ;
|
|
|
|
Memory_table.remove s.memory k ;
|
|
|
|
Lwt.wakeup_later_exn w Lwt.Canceled
|
2017-06-09 19:54:08 +04:00
|
|
|
| Found _ -> Memory_table.remove s.memory k
|
2017-02-24 20:17:53 +04:00
|
|
|
|
|
|
|
let watch s = Watcher.create_stream s.input
|
|
|
|
|
|
|
|
let create ?global_input scheduler disk =
|
|
|
|
let memory = Memory_table.create 17 in
|
|
|
|
let input = Watcher.create_input () in
|
|
|
|
{ scheduler ; disk ; memory ; input ; global_input }
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
module type REQUEST = sig
|
|
|
|
type key
|
|
|
|
type param
|
|
|
|
val active : param -> P2p.Peer_id.Set.t
|
|
|
|
val send : param -> P2p.Peer_id.t -> key list -> unit
|
|
|
|
end
|
|
|
|
|
|
|
|
module Make_request_scheduler
|
2017-09-29 20:43:13 +04:00
|
|
|
(Hash : sig
|
|
|
|
type t
|
|
|
|
val name : string
|
|
|
|
val encoding : t Data_encoding.t
|
|
|
|
val pp : Format.formatter -> t -> unit
|
|
|
|
end)
|
2017-03-28 15:31:41 +04:00
|
|
|
(Table : MEMORY_TABLE with type key := Hash.t)
|
2017-02-24 20:17:53 +04:00
|
|
|
(Request : REQUEST with type key := Hash.t) : sig
|
|
|
|
|
|
|
|
type t
|
|
|
|
val create: Request.param -> t
|
|
|
|
val shutdown: t -> unit Lwt.t
|
|
|
|
include SCHEDULER_EVENTS with type t := t and type key := Hash.t
|
|
|
|
|
|
|
|
end = struct
|
|
|
|
|
2017-09-29 20:43:13 +04:00
|
|
|
include Logging.Make(struct let name = "node.distributed_db.scheduler." ^ Hash.name end)
|
|
|
|
|
2017-02-24 20:17:53 +04:00
|
|
|
type key = Hash.t
|
|
|
|
type param = Request.param
|
|
|
|
|
|
|
|
type t = {
|
|
|
|
push_to_worker: event -> unit ;
|
|
|
|
cancel_worker: unit -> unit Lwt.t ;
|
|
|
|
worker: unit Lwt.t ;
|
|
|
|
}
|
|
|
|
|
|
|
|
and event =
|
|
|
|
| Request of P2p.Peer_id.t option * key
|
|
|
|
| Notify of P2p.Peer_id.t * key
|
2017-11-06 18:23:06 +04:00
|
|
|
| Notify_cancelation of key
|
2017-03-28 15:31:41 +04:00
|
|
|
| Notify_invalid of P2p.Peer_id.t * key
|
2017-02-24 20:17:53 +04:00
|
|
|
| Notify_duplicate of P2p.Peer_id.t * key
|
|
|
|
| Notify_unrequested of P2p.Peer_id.t * key
|
|
|
|
|
|
|
|
let request t p k =
|
|
|
|
t.push_to_worker (Request (p, k))
|
|
|
|
let notify t p k =
|
2017-09-29 20:43:13 +04:00
|
|
|
debug "push received %a from %a"
|
|
|
|
Hash.pp k P2p.Peer_id.pp_short p ;
|
2017-02-24 20:17:53 +04:00
|
|
|
t.push_to_worker (Notify (p, k))
|
2017-11-06 18:23:06 +04:00
|
|
|
let notify_cancelation t k =
|
|
|
|
debug "push cancelation %a"
|
|
|
|
Hash.pp k ;
|
|
|
|
t.push_to_worker (Notify_cancelation k)
|
2017-03-28 15:31:41 +04:00
|
|
|
let notify_invalid t p k =
|
2017-09-29 20:43:13 +04:00
|
|
|
debug "push received invalid %a from %a"
|
|
|
|
Hash.pp k P2p.Peer_id.pp_short p ;
|
2017-03-28 15:31:41 +04:00
|
|
|
t.push_to_worker (Notify_invalid (p, k))
|
2017-02-24 20:17:53 +04:00
|
|
|
let notify_duplicate t p k =
|
2017-09-29 20:43:13 +04:00
|
|
|
debug "push received duplicate %a from %a"
|
|
|
|
Hash.pp k P2p.Peer_id.pp_short p ;
|
2017-02-24 20:17:53 +04:00
|
|
|
t.push_to_worker (Notify_duplicate (p, k))
|
|
|
|
let notify_unrequested t p k =
|
2017-09-29 20:43:13 +04:00
|
|
|
debug "push received unrequested %a from %a"
|
|
|
|
Hash.pp k P2p.Peer_id.pp_short p ;
|
2017-02-24 20:17:53 +04:00
|
|
|
t.push_to_worker (Notify_unrequested (p, k))
|
|
|
|
|
|
|
|
type worker_state = {
|
|
|
|
param: Request.param ;
|
|
|
|
pending: status Table.t ;
|
|
|
|
cancelation: unit -> unit Lwt.t ;
|
|
|
|
wait_events: unit -> event list Lwt.t ;
|
2017-09-29 20:43:13 +04:00
|
|
|
mutable events: event list Lwt.t ;
|
2017-02-24 20:17:53 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
and status = {
|
|
|
|
peers: P2p.Peer_id.Set.t ;
|
|
|
|
next_request: float ;
|
|
|
|
delay: float ;
|
|
|
|
}
|
|
|
|
|
|
|
|
let compute_timeout state =
|
|
|
|
let next =
|
|
|
|
Table.fold
|
|
|
|
(fun _ { next_request } acc -> min next_request acc)
|
|
|
|
state.pending infinity in
|
|
|
|
let now = Unix.gettimeofday () in
|
|
|
|
let delay = next -. now in
|
2017-09-29 20:43:13 +04:00
|
|
|
if delay <= 0. then Lwt.return_unit else begin
|
|
|
|
(* lwt_debug "waiting at least %.2fs" delay >>= fun () -> *)
|
|
|
|
Lwt_unix.sleep delay
|
|
|
|
end
|
|
|
|
|
|
|
|
let may_pp_peer ppf = function
|
|
|
|
| None -> ()
|
|
|
|
| Some peer -> P2p.Peer_id.pp_short ppf peer
|
2017-02-24 20:17:53 +04:00
|
|
|
|
2017-09-29 20:43:13 +04:00
|
|
|
(* TODO should depend on the ressource kind... *)
|
|
|
|
let initial_delay = 0.1
|
|
|
|
|
|
|
|
let process_event state now = function
|
2017-02-24 20:17:53 +04:00
|
|
|
| Request (peer, key) -> begin
|
2017-09-29 20:43:13 +04:00
|
|
|
lwt_debug "registering request %a from %a"
|
|
|
|
Hash.pp key may_pp_peer peer >>= fun () ->
|
2017-02-24 20:17:53 +04:00
|
|
|
try
|
|
|
|
let data = Table.find state.pending key in
|
|
|
|
let peers =
|
|
|
|
match peer with
|
|
|
|
| None -> data.peers
|
|
|
|
| Some peer -> P2p.Peer_id.Set.add peer data.peers in
|
2017-09-29 20:43:13 +04:00
|
|
|
Table.replace state.pending key {
|
|
|
|
delay = initial_delay ;
|
|
|
|
next_request = min data.next_request (now +. initial_delay) ;
|
|
|
|
peers ;
|
|
|
|
} ;
|
2017-09-29 20:43:13 +04:00
|
|
|
lwt_debug "registering request %a from %a -> replaced"
|
|
|
|
Hash.pp key may_pp_peer peer >>= fun () ->
|
2017-02-24 20:17:53 +04:00
|
|
|
Lwt.return_unit
|
|
|
|
with Not_found ->
|
|
|
|
let peers =
|
|
|
|
match peer with
|
|
|
|
| None -> P2p.Peer_id.Set.empty
|
|
|
|
| Some peer -> P2p.Peer_id.Set.singleton peer in
|
|
|
|
Table.add state.pending key {
|
|
|
|
peers ;
|
2017-09-29 20:43:13 +04:00
|
|
|
next_request = now ;
|
|
|
|
delay = initial_delay ;
|
2017-02-24 20:17:53 +04:00
|
|
|
} ;
|
2017-09-29 20:43:13 +04:00
|
|
|
lwt_debug "registering request %a from %a -> added"
|
|
|
|
Hash.pp key may_pp_peer peer >>= fun () ->
|
2017-02-24 20:17:53 +04:00
|
|
|
Lwt.return_unit
|
|
|
|
end
|
2017-09-29 20:43:13 +04:00
|
|
|
| Notify (peer, key) ->
|
2017-02-24 20:17:53 +04:00
|
|
|
Table.remove state.pending key ;
|
2017-09-29 20:43:13 +04:00
|
|
|
lwt_debug "received %a from %a"
|
|
|
|
Hash.pp key P2p.Peer_id.pp_short peer >>= fun () ->
|
|
|
|
Lwt.return_unit
|
2017-11-06 18:23:06 +04:00
|
|
|
| Notify_cancelation key ->
|
|
|
|
Table.remove state.pending key ;
|
|
|
|
lwt_debug "canceled %a"
|
|
|
|
Hash.pp key >>= fun () ->
|
|
|
|
Lwt.return_unit
|
2017-09-29 20:43:13 +04:00
|
|
|
| Notify_invalid (peer, key) ->
|
|
|
|
lwt_debug "received invalid %a from %a"
|
|
|
|
Hash.pp key P2p.Peer_id.pp_short peer >>= fun () ->
|
|
|
|
(* TODO *)
|
|
|
|
Lwt.return_unit
|
|
|
|
| Notify_unrequested (peer, key) ->
|
|
|
|
lwt_debug "received unrequested %a from %a"
|
|
|
|
Hash.pp key P2p.Peer_id.pp_short peer >>= fun () ->
|
|
|
|
(* TODO *)
|
2017-02-24 20:17:53 +04:00
|
|
|
Lwt.return_unit
|
2017-09-29 20:43:13 +04:00
|
|
|
| Notify_duplicate (peer, key) ->
|
|
|
|
lwt_debug "received duplicate %a from %a"
|
|
|
|
Hash.pp key P2p.Peer_id.pp_short peer >>= fun () ->
|
2017-02-24 20:17:53 +04:00
|
|
|
(* TODO *)
|
|
|
|
Lwt.return_unit
|
|
|
|
|
2017-09-29 20:43:13 +04:00
|
|
|
let rec worker_loop state =
|
|
|
|
let shutdown = state.cancelation ()
|
|
|
|
and timeout = compute_timeout state in
|
|
|
|
Lwt.choose [ (state.events >|= fun _ -> ()) ; timeout ; shutdown ] >>= fun () ->
|
|
|
|
if Lwt.state shutdown <> Lwt.Sleep then
|
2017-09-29 20:43:13 +04:00
|
|
|
lwt_debug "terminating" >>= fun () ->
|
2017-09-29 20:43:13 +04:00
|
|
|
Lwt.return_unit
|
|
|
|
else if Lwt.state state.events <> Lwt.Sleep then
|
2017-09-29 20:43:13 +04:00
|
|
|
let now = Unix.gettimeofday () in
|
2017-09-29 20:43:13 +04:00
|
|
|
state.events >>= fun events ->
|
|
|
|
state.events <- state.wait_events () ;
|
2017-09-29 20:43:13 +04:00
|
|
|
Lwt_list.iter_s (process_event state now) events >>= fun () ->
|
2017-09-29 20:43:13 +04:00
|
|
|
worker_loop state
|
|
|
|
else
|
2017-09-29 20:43:13 +04:00
|
|
|
lwt_debug "timeout" >>= fun () ->
|
2017-09-29 20:43:13 +04:00
|
|
|
let now = Unix.gettimeofday () in
|
|
|
|
let active_peers = Request.active state.param in
|
|
|
|
let requests =
|
|
|
|
Table.fold
|
|
|
|
(fun key { peers ; next_request ; delay } acc ->
|
|
|
|
if next_request > now +. 0.2 then
|
|
|
|
acc
|
|
|
|
else
|
|
|
|
let remaining_peers =
|
|
|
|
P2p.Peer_id.Set.inter peers active_peers in
|
|
|
|
if P2p.Peer_id.Set.is_empty remaining_peers &&
|
|
|
|
not (P2p.Peer_id.Set.is_empty peers) then
|
|
|
|
( Table.remove state.pending key ; acc )
|
|
|
|
else
|
2017-09-29 20:43:13 +04:00
|
|
|
let requested_peer =
|
|
|
|
P2p.Peer_id.random_set_elt
|
|
|
|
(if P2p.Peer_id.Set.is_empty remaining_peers
|
|
|
|
then active_peers
|
|
|
|
else remaining_peers) in
|
2017-09-29 20:43:13 +04:00
|
|
|
let next = { peers = remaining_peers ;
|
|
|
|
next_request = now +. delay ;
|
|
|
|
delay = delay *. 1.2 } in
|
|
|
|
Table.replace state.pending key next ;
|
2017-09-29 20:43:13 +04:00
|
|
|
let requests =
|
|
|
|
try key :: P2p_types.Peer_id.Map.find requested_peer acc
|
|
|
|
with Not_found -> [key] in
|
|
|
|
P2p_types.Peer_id.Map.add requested_peer requests acc)
|
2017-09-29 20:43:13 +04:00
|
|
|
state.pending P2p_types.Peer_id.Map.empty in
|
|
|
|
P2p_types.Peer_id.Map.iter (Request.send state.param) requests ;
|
2017-09-29 20:43:13 +04:00
|
|
|
P2p_types.Peer_id.Map.fold begin fun peer request acc ->
|
|
|
|
acc >>= fun () ->
|
|
|
|
Lwt_list.iter_s (fun key ->
|
|
|
|
lwt_debug "requested %a from %a"
|
|
|
|
Hash.pp key P2p.Peer_id.pp_short peer)
|
|
|
|
request
|
|
|
|
end requests Lwt.return_unit >>= fun () ->
|
2017-09-29 20:43:13 +04:00
|
|
|
worker_loop state
|
2017-02-24 20:17:53 +04:00
|
|
|
|
|
|
|
let create param =
|
|
|
|
let cancelation, cancel_worker, _ = Lwt_utils.canceler () in
|
|
|
|
let push_to_worker, wait_events = Lwt_utils.queue () in
|
|
|
|
let pending = Table.create 17 in
|
|
|
|
let worker_state =
|
2017-09-29 20:43:13 +04:00
|
|
|
{ cancelation ; wait_events ; pending ; param ; events = wait_events () } in
|
2017-02-24 20:17:53 +04:00
|
|
|
let worker =
|
|
|
|
Lwt_utils.worker "db_request_scheduler"
|
2017-09-29 20:43:13 +04:00
|
|
|
~run:(fun () -> worker_loop worker_state)
|
2017-02-24 20:17:53 +04:00
|
|
|
~cancel:cancel_worker in
|
|
|
|
{ cancel_worker ; push_to_worker ; worker }
|
|
|
|
|
|
|
|
let shutdown s =
|
|
|
|
s.cancel_worker () >>= fun () ->
|
|
|
|
s.worker
|
|
|
|
|
|
|
|
end
|