package caqti
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
Unified interface to relational database libraries
Install
dune-project
Dependency
Authors
Maintainers
Sources
caqti-v3.0.0.tbz
sha256=3ceea06ba0e8e8bcab0386b5817b68cb30fce457eaa25ada0182891d66f6a0b9
sha512=e01f546a45b04cafd5fa262e9228e5a1aabd5e0942059c073a4181c928450baf3d990cc59ffb1a33dff445b46a0e218224a23dca96e8d24cdd0ab793d08e4538
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 223 224 225 226 227 228 229 230 231 232 233 234(* Copyright (C) 2025--2026 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 (* OCaml 4.12 *) let rec equal_list eq xs ys = (match xs, ys with | [], [] -> true | x :: xs, y :: ys -> eq x y && equal_list eq xs ys | [], _ :: _ | _ :: _, [] -> false) module Key = struct type t = T : { param_type: 'a Row_type.t; row_type: 'b Row_type.t; row_mult: 'm Row_mult.t; queries: Query.t list; } -> t let create request dialect = T { param_type = Request.param_type request; row_type = Request.row_type request; row_mult = Request.row_mult request; queries = Request.queries request dialect; } let equal (T k1) (T k2) = equal_list Query.equal k1.queries k2.queries && 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. *) Hashtbl.hash k.queries 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 request_alive = | Request_alive : (_, _, _) Request.t Weak.t -> request_alive type t = { elt: Elt.t; request_alive: request_alive; } let create request elt = let w = Weak.create 1 in Weak.set w 0 (Some request); {elt; request_alive = Request_alive w} let is_alive node = let Request_alive w = node.request_alive in Weak.check w 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)"
>