package caqti
Unified interface to relational database libraries
Install
Dune Dependency
Authors
Maintainers
Sources
caqti-v2.2.4.tbz
sha256=b8ea432820154ec095132c4f7b244b06cd8553e0b2035185b844d9c4f30af8bb
sha512=b7e3ad8e6a9b587db2d517e15cd42df2945148f9223b2fa6f4bc2bcdd2709d53549cca4b65e54511d22466e4c9aa7f0b9c17305a07505519d8bf81d95de629b8
doc/src/caqti.platform/request_cache.ml.html
Source file request_cache.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
(* Copyright (C) 2025 Petter A. Urkedal <paurkedal@gmail.com> * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at your * option) any later version, with the LGPL-3.0 Linking Exception. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * and the LGPL-3.0 Linking Exception along with this library. If not, see * <http://www.gnu.org/licenses/> and <https://spdx.org>, respectively. *) [@@@alert "-caqti_private"] open Caqti_template module type S = sig type elt type t val create : ?dynamic_capacity: int -> Dialect.t -> t val find_and_promote : t -> ('a, 'b, 'm) Request.t -> elt option val add : t -> ('a, 'b, 'm) Request.t -> elt -> unit val remove_and_discard : t -> ('a, 'b, 'm) Request.t -> unit val deallocate : t -> ('a, 'b, 'm) Request.t -> (elt * (unit -> unit)) option val iter : (elt -> unit) -> t -> unit val elements : t -> elt list val trim : ?max_promote_count: int -> t -> elt list * (unit -> unit) val clear_and_discard : t -> unit val dynamic_weight : t -> int end module Key = struct type t = T : { param_type: 'a Row_type.t; row_type: 'b Row_type.t; row_mult: 'm Row_mult.t; query: Query.t; } -> t let create request dialect = T { param_type = Request.param_type request; row_type = Request.row_type request; row_mult = Request.row_mult request; query = Request.query request dialect; } let equal (T k1) (T k2) = Query.equal k1.query k2.query && Row_type.unify k1.param_type k2.param_type <> None && Row_type.unify k1.row_type k2.row_type <> None let hash (T k) = (* TODO: Consider also hashing over the types. *) Query.hash k.query end let is_static request = (match Request.prepare_policy request with | Request.Direct -> failwith "Prepare_cache must not be used with direct requests." | Request.Dynamic -> false | Request.Static -> true) module Make (Elt : Lru.Weighted) = struct type elt = Elt.t (* This module adds a weak pointer to the request template, so that we can * promote the associated prepare query in the LRU cache if the request has * not been garbage collected. To avoid extra engineering with limited * gain, we only track the first request producing a certain key. This seems * better than tracking the latest request in the case there are a mixture of * short- and long-lived request, since it gives longer lived requests a * better chance of holding on to the liveness slot. *) module Dynamic_node = struct type t = { elt: Elt.t; liveness_witness: Request.liveness_witness Weak.t; } let create request elt = let liveness_witness = Weak.create 1 in Weak.set liveness_witness 0 (Some (Request.liveness_witness request)); {elt; liveness_witness} let is_alive node = Weak.check node.liveness_witness 0 let elt node = node.elt let weight node = Elt.weight node.elt end module Static_cache = Hashtbl.Make (Key) module Dynamic_cache = Lru.M.Make (Key) (Dynamic_node) type t = { dialect: Caqti_template.Dialect.t; static_cache: Elt.t Static_cache.t; dynamic_cache: Dynamic_cache.t; mutable dynamic_orphans: Elt.t list; } let create ?(dynamic_capacity = 20) dialect = { dialect; static_cache = Static_cache.create 11; dynamic_cache = Dynamic_cache.create dynamic_capacity; dynamic_orphans = []; } let find_and_promote cache request = let key = Key.create request cache.dialect in if is_static request then (* Try the static map first, then the dynamic map. If found in the * latter, move the binding to the former, since we have a witness of the * static lifetime of the associated prepared query. *) (match Static_cache.find_opt cache.static_cache key with | None -> (match Dynamic_cache.find key cache.dynamic_cache with | None -> None | Some node -> let elt = Dynamic_node.elt node in Static_cache.add cache.static_cache key elt; Dynamic_cache.remove key cache.dynamic_cache; Some elt) | Some elt -> Some elt) else (* Try the dynamic map first, then the static map. *) (match Dynamic_cache.find key cache.dynamic_cache with | None -> Static_cache.find_opt cache.static_cache key | Some node -> Dynamic_cache.promote key cache.dynamic_cache; Some (Dynamic_node.elt node)) let rec trim' ~max_promote_count cache = let cap = Dynamic_cache.capacity cache.dynamic_cache in if Dynamic_cache.weight cache.dynamic_cache > cap then (match Dynamic_cache.lru cache.dynamic_cache with | None -> assert false | Some (key, node) when Dynamic_node.is_alive node -> if max_promote_count > 0 then begin Dynamic_cache.promote key cache.dynamic_cache; trim' ~max_promote_count:(max_promote_count - 1) cache end | Some (_, node) -> cache.dynamic_orphans <- node.elt :: cache.dynamic_orphans; Dynamic_cache.drop_lru cache.dynamic_cache; trim' ~max_promote_count cache) let trim ?(max_promote_count = 1) cache = trim' ~max_promote_count cache; (cache.dynamic_orphans, (fun () -> cache.dynamic_orphans <- [])) let add cache request elt = trim' ~max_promote_count:0 cache; let key = Key.create request cache.dialect in assert (not (Static_cache.mem cache.static_cache key)); assert (not (Dynamic_cache.mem key cache.dynamic_cache)); if is_static request then Static_cache.add cache.static_cache key elt else let node = Dynamic_node.create request elt in Dynamic_cache.add key node cache.dynamic_cache let remove_and_discard cache request = let key = Key.create request cache.dialect in if is_static request then begin assert (Static_cache.mem cache.static_cache key); Static_cache.remove cache.static_cache key end else begin assert (Dynamic_cache.mem key cache.dynamic_cache); Dynamic_cache.remove key cache.dynamic_cache end let deallocate cache request = let key = Key.create request cache.dialect in if is_static request then (match Static_cache.find_opt cache.static_cache key with | None -> None | Some elt -> let commit () = Static_cache.remove cache.static_cache key in Some (elt, commit)) else (match Dynamic_cache.find key cache.dynamic_cache with | None -> None | Some node -> let commit () = Dynamic_cache.remove key cache.dynamic_cache in Some (node.elt, commit)) let iter f cache = Static_cache.iter (Fun.const f) cache.static_cache; Dynamic_cache.iter (fun _ node -> f node.elt) cache.dynamic_cache let elements cache = let add_static _ elt acc = elt :: acc in let add_dynamic _ node acc = node.Dynamic_node.elt :: acc in [] |> Static_cache.fold add_static cache.static_cache |> Fun.flip (Dynamic_cache.fold add_dynamic) cache.dynamic_cache let clear_and_discard cache = Static_cache.clear cache.static_cache; let cap = Dynamic_cache.capacity cache.dynamic_cache in Dynamic_cache.resize 0 cache.dynamic_cache; Dynamic_cache.trim cache.dynamic_cache; Dynamic_cache.resize cap cache.dynamic_cache let dynamic_weight cache = Dynamic_cache.weight cache.dynamic_cache end
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>