Skip to content

Commit 23b1bf7

Browse files
author
Jan Rochel
committed
cache queries in a hash table
1 parent dc9a242 commit 23b1bf7

File tree

2 files changed

+48
-19
lines changed

2 files changed

+48
-19
lines changed

doc/manual-wiki/libraries.wiki

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ Note that the ocsipersist-database under the specified name (default:
4646
password="Guess what I need!"
4747
database="ocsipersist" <!-- this is the default value if not specified -->
4848
unix_domain_socket_dir="./udsd"
49+
hashtbl_size="16" <!-- rule of thumb: 8 * number of persistent values / tables -->
4950
/>
5051
</extension>
5152
}}}

src/extensions/ocsipersist-pgsql/ocsipersist.ml

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,31 @@ let host = ref None
1616
let port = ref None
1717
let user = ref None
1818
let password = ref None
19-
let database = ref (Some "ocsipersist")
19+
let database = ref "ocsipersist"
2020
let unix_domain_socket_dir = ref None
21+
let hashtbl_size = ref 8
2122

22-
let connect () = PGOCaml.connect
23+
let make_hashtbl () = Hashtbl.create !hashtbl_size
24+
25+
let connect () =
26+
lwt dbhandle = PGOCaml.connect
2327
?host:!host
2428
?port:!port
2529
?user:!user
2630
?password:!password
27-
?database:!database
31+
?database:(Some !database)
2832
?unix_domain_socket_dir:!unix_domain_socket_dir
29-
()
33+
() in
34+
PGOCaml.set_private_data dbhandle @@ make_hashtbl ();
35+
Lwt.return dbhandle
3036

3137
let (>>) f g = f >>= fun _ -> g
3238

33-
let pool : (string, bool) Hashtbl.t PGOCaml.t Lwt_pool.t =
39+
let pool : (string, unit) Hashtbl.t PGOCaml.t Lwt_pool.t =
3440
Lwt_pool.create 16 ~validate:PGOCaml.alive connect
3541

3642
let use_pool f = Lwt_pool.use pool (fun db -> f db)
3743

38-
let exec db query params =
39-
PGOCaml.prepare db ~query () >>
40-
let params = params |> List.map @@ fun x -> Some (PGOCaml.string_of_bytea x) in
41-
PGOCaml.execute db ~params ()
42-
43-
let (@.) f g = fun x -> f (g x) (* function composition *)
44-
4544
let key_value_of_row = function
4645
| [Some key; Some value] -> (PGOCaml.bytea_of_string key, PGOCaml.bytea_of_string value)
4746
| _ -> raise Ocsipersist_error
@@ -54,6 +53,33 @@ let one = function
5453
let marshal value = Marshal.to_string value []
5554
let unmarshal str = Marshal.from_string str 0
5655

56+
let prepare db query =
57+
let hashtbl = PGOCaml.private_data db in
58+
(* Get a unique name for this query using an MD5 digest. *)
59+
let name = Digest.to_hex (Digest.string query) in
60+
(* Have we prepared this statement already? If not, do so. *)
61+
let is_prepared = Hashtbl.mem hashtbl name in
62+
lwt () = if is_prepared then Lwt.return () else begin
63+
PGOCaml.prepare db ~name ~query () >>
64+
Lwt.return @@ Hashtbl.add hashtbl name ()
65+
end in
66+
Lwt.return name
67+
68+
let exec db query params =
69+
lwt name = prepare db query in
70+
let params = params |> List.map @@ fun x -> Some (PGOCaml.string_of_bytea x) in
71+
PGOCaml.execute db ~name ~params ()
72+
73+
let cursor db query params f =
74+
lwt name = prepare db query in
75+
let params = params |> List.map @@ fun x -> Some (PGOCaml.string_of_bytea x) in
76+
PGOCaml.cursor db ~name ~params @@
77+
fun row -> let (key,value) = key_value_of_row row in f
78+
(PGOCaml.bytea_of_string key)
79+
(unmarshal @@ PGOCaml.bytea_of_string value)
80+
81+
let (@.) f g = fun x -> f (g x) (* function composition *)
82+
5783
let create_table db table =
5884
let query = sprintf "CREATE TABLE IF NOT EXISTS %s \
5985
(key TEXT, value BYTEA, PRIMARY KEY(key))" table
@@ -64,6 +90,7 @@ let insert db table key value =
6490
ON CONFLICT ( key ) DO UPDATE SET value = $2 " table
6591
in exec db query [key; marshal value] >> Lwt.return ()
6692

93+
6794
type store = string
6895

6996
type 'a t = {
@@ -77,7 +104,7 @@ let open_store store = use_pool @@ fun db ->
77104
let make_persistent_lazy_lwt ~store ~name ~default = use_pool @@ fun db ->
78105
let query = sprintf "SELECT value FROM %s WHERE key = $1 " store in
79106
lwt result = exec db query [name] in
80-
lwt _ = begin match result with
107+
lwt () = begin match result with
81108
| [] ->
82109
lwt default = default () in
83110
insert db store name default
@@ -129,11 +156,7 @@ let length table = use_pool @@ fun db ->
129156

130157
let iter_step f table = use_pool @@ fun db ->
131158
let query = sprintf "SELECT * FROM %s " table in
132-
PGOCaml.prepare db ~query () >>
133-
PGOCaml.cursor db ~params:[] @@
134-
fun row -> let (key,value) = key_value_of_row row in f
135-
(PGOCaml.bytea_of_string key)
136-
(unmarshal @@ PGOCaml.bytea_of_string value)
159+
cursor db query [] f
137160

138161
let iter_table = iter_step
139162

@@ -162,8 +185,13 @@ let parse_global_config = function
162185
end
163186
| ("user", u) -> user := Some u
164187
| ("password", pw) -> password := Some pw
165-
| ("database", db) -> database := Some db
188+
| ("database", db) -> database := db
166189
| ("unix_domain_socket_dir", udsd) -> unix_domain_socket_dir := Some udsd
190+
| ("hashtbl_size", hts) -> begin
191+
try hashtbl_size := int_of_string hts
192+
with Failure _ -> raise @@ Ocsigen_extensions.Error_in_config_file
193+
"hashtbl_size is not an integer"
194+
end
167195
| _ -> raise @@ Ocsigen_extensions.Error_in_config_file
168196
"Unexpected attribute for <database> in Ocsipersist config"
169197
in ignore @@ List.map parse_attr attrs; ()

0 commit comments

Comments
 (0)