aboutsummaryrefslogtreecommitdiffstats
path: root/bin
diff options
context:
space:
mode:
authorMarcos Magueta <maguetamarcos@gmail.com>2026-04-03 16:17:38 -0300
committerMarcos Magueta <maguetamarcos@gmail.com>2026-04-03 16:17:38 -0300
commit63ae3c930de89af6f85229a467caa3f53cb844a2 (patch)
treecda4d513672f9fefa09c3a1dff69d8443890e224 /bin
parentc5bc3a4992b4968780c286a084ad7c02b2b58f1e (diff)
downloadSakura-63ae3c930de89af6f85229a467caa3f53cb844a2.tar.gz
Sakura-63ae3c930de89af6f85229a467caa3f53cb844a2.zip
format
Diffstat (limited to '')
-rw-r--r--bin/bench.ml202
-rw-r--r--bin/main.ml18
-rw-r--r--bin/server.ml339
3 files changed, 188 insertions, 371 deletions
diff --git a/bin/bench.ml b/bin/bench.ml
deleted file mode 100644
index 8f496f2..0000000
--- a/bin/bench.ml
+++ /dev/null
@@ -1,202 +0,0 @@
-open Relational_engine
-
-(* helpers *)
-
-let time label f =
- let t0 = Unix.gettimeofday () in
- f ();
- let elapsed = Unix.gettimeofday () -. t0 in
- Printf.printf " %-60s %.4f s\n%!" label elapsed
-
-let mk_int (n : int) : Conventions.AbstractValue.t = Obj.magic n
-
-let make_tuple relation pairs =
- let attributes =
- List.fold_left
- (fun acc (k, v) ->
- Tuple.AttributeMap.add k { Attribute.value = mk_int v } acc)
- Tuple.AttributeMap.empty
- pairs
- in
- { Tuple.relation; attributes }
-
-let unwrap label = function
- | Ok v -> v
- | Error _ -> failwith label
-
-let get_rel db name =
- match Manipulation.Memory.get_relation db ~name with
- | Some r -> r
- | None -> failwith ("relation not found: " ^ name)
-
-(* database setup *)
-
-(*
- Schema:
- Department { dept_id }
- Employee { emp_id, dept_id }
-
- Constraint on Employee:
- Exists d in Department, MemberOf Department (dept_id = Var "dept_id")
-
- This is a classic FK: every employee must reference an existing department.
- On the sublanguages branch, deleting a referenced Department triggers a
- cascade re-check of affected Employee tuples and rejects the delete.
- On master (no cascade), the delete succeeds unconditionally.
-*)
-let setup storage ~n_depts ~employees_per_dept =
- let db =
- unwrap "create_database"
- (Manipulation.Memory.create_database storage ~name:"bench")
- in
- let db =
- fst (unwrap "create Department"
- (Manipulation.Memory.create_relation storage db ~name:"Department"
- ~schema:(Schema.empty |> Schema.add "dept_id" "natural")))
- in
- let db =
- fst (unwrap "create Employee"
- (Manipulation.Memory.create_relation storage db ~name:"Employee"
- ~schema:(Schema.empty
- |> Schema.add "emp_id" "natural"
- |> Schema.add "dept_id" "natural")))
- in
- let fk_body =
- Constraint.Exists {
- variable = "d";
- quantifier = "Department";
- body = Constraint.MemberOf {
- target = "Department";
- binding = Constraint.BindingMap.singleton "dept_id" (Constraint.Var "dept_id");
- };
- }
- in
- let db =
- unwrap "register_constraint"
- (Manipulation.Memory.register_constraint storage db
- ~constraint_name:"fk_dept"
- ~relation_name:"Employee"
- ~body:fk_body)
- in
- let dept_tuples =
- List.init n_depts (fun i ->
- make_tuple "Department" [("dept_id", i + 1)])
- in
- let db =
- let (db, _, _) = unwrap "insert departments"
- (Manipulation.Memory.create_tuples storage db
- (get_rel db "Department") dept_tuples)
- in db
- in
- let emp_tuples =
- List.init (n_depts * employees_per_dept) (fun i ->
- make_tuple "Employee" [
- ("emp_id", i + 1);
- ("dept_id", (i mod n_depts) + 1);
- ])
- in
- let db =
- let (db, _, _) = unwrap "insert employees"
- (Manipulation.Memory.create_tuples storage db
- (get_rel db "Employee") emp_tuples)
- in db
- in
- db
-
-(* benchmark cases *)
-
-(*
- Attempt to delete each department that has employees referencing it.
-
- sublanguages: retract_tuple runs the cascade check — uses polarity analysis
- to identify Employee as the constrained relation, then focused_filter to
- narrow the scan to only employees with dept_id = deleted dept_id. Finds a
- match → returns ConstraintViolation. Delete is rejected.
-
- master: no cascade logic; retract_tuple succeeds unconditionally and db
- becomes inconsistent.
-
- db is not threaded through since on sublanguages every delete is rejected
- (db state is unchanged after each Error), and on master we deliberately
- want to measure per-delete cost against a consistent employee set.
-*)
-let bench_retract_referenced storage db ~n_depts =
- let rel = get_rel db "Department" in
- for i = 1 to n_depts do
- let hash = Hashing.hash_tuple (make_tuple "Department" [("dept_id", i)]) in
- ignore (Manipulation.Memory.retract_tuple storage db rel ~tuple_hash:hash)
- done
-
-(*
- Delete departments that have NO referencing employees. Neither branch
- needs to scan Employee tuples (focused_filter finds no matches), so this
- measures raw retract_tuple cost: Merkle tree update + storage write.
-
- We thread db through the loop so each delete builds on the previous state
- and only one live tree version accumulates in memory at a time.
-
- Orphan dept ids start at offset to avoid colliding with the employee-owned
- depts (1..n_depts).
-*)
-let bench_retract_unreferenced storage db ~n_depts ~offset =
- let orphan_tuples =
- List.init n_depts (fun i ->
- make_tuple "Department" [("dept_id", offset + i + 1)])
- in
- let db =
- let (db, _, _) = unwrap "insert orphan depts"
- (Manipulation.Memory.create_tuples storage db
- (get_rel db "Department") orphan_tuples)
- in db
- in
- let _db =
- List.fold_left (fun db i ->
- let rel = get_rel db "Department" in
- let hash = Hashing.hash_tuple (make_tuple "Department" [("dept_id", offset + i)]) in
- match Manipulation.Memory.retract_tuple storage db rel ~tuple_hash:hash with
- | Ok (db, _) -> db
- | Error _ -> db)
- db
- (List.init n_depts (fun i -> i + 1))
- in
- ()
-
-(* entry point *)
-
-let () =
- let n_depts = 100 in
- let employees_per_dept = 200 in
- let total_employees = n_depts * employees_per_dept in
-
- Printf.printf "\nSakura constraint benchmark\n";
- Printf.printf " Departments : %d\n" n_depts;
- Printf.printf " Employees/dept : %d (%d total)\n\n"
- employees_per_dept total_employees;
-
- let make_storage () =
- match Management.Physical.Memory.create () with
- | Ok s -> s
- | Error _ -> failwith "create storage"
- in
-
- (* Warmup build — not measured *)
- let storage = make_storage () in
- ignore (setup storage ~n_depts ~employees_per_dept);
-
- (* Fresh storage for measured runs *)
- let storage = make_storage () in
- let db = setup storage ~n_depts ~employees_per_dept in
-
- Printf.printf "Scenarios:\n";
-
- time (Printf.sprintf
- "retract %d referenced depts (cascade check, violations expected)"
- n_depts)
- (fun () -> bench_retract_referenced storage db ~n_depts);
-
- time (Printf.sprintf
- "retract %d unreferenced depts (no matching employees)"
- n_depts)
- (fun () -> bench_retract_unreferenced storage db ~n_depts ~offset:1_000_000);
-
- Printf.printf "\n"
diff --git a/bin/main.ml b/bin/main.ml
index 46bb9f1..c77d756 100644
--- a/bin/main.ml
+++ b/bin/main.ml
@@ -1,11 +1,17 @@
let main () =
- let digest = Relational_engine.Interop.Sha256.compute_hash (Bytes.of_string "sakura") in
+ let digest =
+ Relational_engine.Interop.Sha256.compute_hash (Bytes.of_string "sakura")
+ in
print_endline digest
let () =
- let tuple =
- Relational_engine.Tuple.make_materialized
- ~relation: "natural_less_than"
- ~attributes: (Relational_engine.Tuple.AttributeMap.of_list [("left", Obj.magic 0); ("right", Obj.magic 1)])
- in print_endline @@ string_of_bool @@ Relational_engine.Prelude.Standard.less_than_natural.membership_criteria tuple;
+ let tuple =
+ Relational_engine.Tuple.make_materialized ~relation:"natural_less_than"
+ ~attributes:
+ (Relational_engine.Tuple.AttributeMap.of_list
+ [ ("left", Obj.magic 0); ("right", Obj.magic 1) ])
+ in
+ print_endline @@ string_of_bool
+ @@ Relational_engine.Prelude.Standard.less_than_natural.membership_criteria
+ tuple;
main ()
diff --git a/bin/server.ml b/bin/server.ml
index 2b9e651..82965dc 100644
--- a/bin/server.ml
+++ b/bin/server.ml
@@ -15,16 +15,17 @@ let materialize_generator gen limit =
if count >= limit then (List.rev acc, true)
else
match gen (Some pos) with
- | Generator.Done -> (List.rev acc, false)
- | Generator.Error _ -> (List.rev acc, false)
- | Generator.Value (t, next) ->
- let mat = match t with
- | Tuple.Materialized m -> Some m
- | Tuple.NonMaterialized _ -> None
- in
- (match mat with
- | None -> go next (pos + 1) acc count
- | Some m -> go next (pos + 1) (m :: acc) (count + 1))
+ | Generator.Done -> (List.rev acc, false)
+ | Generator.Error _ -> (List.rev acc, false)
+ | Generator.Value (t, next) -> (
+ let mat =
+ match t with
+ | Tuple.Materialized m -> Some m
+ | Tuple.NonMaterialized _ -> None
+ in
+ match mat with
+ | None -> go next (pos + 1) acc count
+ | Some m -> go next (pos + 1) (m :: acc) (count + 1))
in
go gen 0 [] 0
@@ -34,106 +35,116 @@ let materialize_relation storage (rel : Relation.t) limit =
let short_hash (h : string) = String.sub h 0 (min 8 (String.length h))
-module BranchOps = Management.Branch.Make(Management.Physical.Memory)
-module Dispatch = Dbms_language.Memory
+module BranchOps = Management.Branch.Make (Management.Physical.Memory)
+module Dispatch = Dbms_language.Memory
let get_branch storage =
- match BranchOps.get_head storage with
- | Ok (Some name) -> name
- | _ -> "--"
+ match BranchOps.get_head storage with Ok (Some name) -> name | _ -> "--"
let tuple_to_sexp (t : Tuple.materialized) =
let open Sexplib.Sexp in
- List (Tuple.AttributeMap.bindings t.Tuple.attributes
- |> List.map (fun (k, attr) ->
- List [Atom k; Conventions.AbstractValue.sexp_of_t attr.Attribute.value]))
+ List
+ (Tuple.AttributeMap.bindings t.Tuple.attributes
+ |> List.map (fun (k, attr) ->
+ List
+ [ Atom k; Conventions.AbstractValue.sexp_of_t attr.Attribute.value ])
+ )
let relation_to_sexp storage db_name db_hash (rel : Relation.t) limit =
let open Sexplib.Sexp in
let schema_sexp =
- List (List.map (fun (a, d) -> List [Atom a; Atom d]) rel.Relation.schema)
+ List (List.map (fun (a, d) -> List [ Atom a; Atom d ]) rel.Relation.schema)
in
- let (tuples, truncated) = materialize_relation storage rel limit in
+ let tuples, truncated = materialize_relation storage rel limit in
let rows_sexp = List (List.map tuple_to_sexp tuples) in
- to_string @@ List [
- Atom "relation";
- List [Atom "name"; Atom rel.Relation.name];
- List [Atom "schema"; schema_sexp];
- List [Atom "rows"; rows_sexp];
- List [Atom "row_count"; Atom (string_of_int (List.length tuples))];
- List [Atom "truncated"; Atom (string_of_bool truncated)];
- List [Atom "db_hash"; Atom (short_hash db_hash)];
- List [Atom "db_name"; Atom db_name];
- List [Atom "branch"; Atom (get_branch storage)];
- ]
+ to_string
+ @@ List
+ [
+ Atom "relation";
+ List [ Atom "name"; Atom rel.Relation.name ];
+ List [ Atom "schema"; schema_sexp ];
+ List [ Atom "rows"; rows_sexp ];
+ List [ Atom "row_count"; Atom (string_of_int (List.length tuples)) ];
+ List [ Atom "truncated"; Atom (string_of_bool truncated) ];
+ List [ Atom "db_hash"; Atom (short_hash db_hash) ];
+ List [ Atom "db_name"; Atom db_name ];
+ List [ Atom "branch"; Atom (get_branch storage) ];
+ ]
let ok_response storage db_name db_hash msg =
let open Sexplib.Sexp in
- to_string @@ List [
- Atom "ok";
- List [Atom "message"; Atom msg];
- List [Atom "db_hash"; Atom (short_hash db_hash)];
- List [Atom "db_name"; Atom db_name];
- List [Atom "branch"; Atom (get_branch storage)];
- ]
+ to_string
+ @@ List
+ [
+ Atom "ok";
+ List [ Atom "message"; Atom msg ];
+ List [ Atom "db_hash"; Atom (short_hash db_hash) ];
+ List [ Atom "db_name"; Atom db_name ];
+ List [ Atom "branch"; Atom (get_branch storage) ];
+ ]
let error_response storage db_name db_hash err_sexp =
let open Sexplib.Sexp in
- to_string @@ List [
- Atom "error";
- err_sexp;
- List [Atom "db_hash"; Atom (short_hash db_hash)];
- List [Atom "db_name"; Atom db_name];
- List [Atom "branch"; Atom (get_branch storage)];
- ]
+ to_string
+ @@ List
+ [
+ Atom "error";
+ err_sexp;
+ List [ Atom "db_hash"; Atom (short_hash db_hash) ];
+ List [ Atom "db_name"; Atom db_name ];
+ List [ Atom "branch"; Atom (get_branch storage) ];
+ ]
let advance_head_branch storage new_hash =
match BranchOps.get_head storage with
| Ok (Some branch_name) ->
- ignore (BranchOps.update_tip storage ~name:branch_name ~tip:new_hash)
+ ignore (BranchOps.update_tip storage ~name:branch_name ~tip:new_hash)
| _ -> ()
-let dbms_dispatch = Dispatch.create [
- (module Drl.Sublanguage.Memory : Dispatch.SubS);
- (module Ddl.Sublanguage.Memory : Dispatch.SubS);
- (module Dml.Sublanguage.Memory : Dispatch.SubS);
- (module Icl.Sublanguage.Memory : Dispatch.SubS);
- (module Dcl.Sublanguage.Memory : Dispatch.SubS);
- (module Scl.Sublanguage.Memory : Dispatch.SubS);
-]
+let dbms_dispatch =
+ Dispatch.create
+ [
+ (module Drl.Sublanguage.Memory : Dispatch.SubS);
+ (module Ddl.Sublanguage.Memory : Dispatch.SubS);
+ (module Dml.Sublanguage.Memory : Dispatch.SubS);
+ (module Icl.Sublanguage.Memory : Dispatch.SubS);
+ (module Dcl.Sublanguage.Memory : Dispatch.SubS);
+ (module Scl.Sublanguage.Memory : Dispatch.SubS);
+ ]
let cursor_to_sexp storage db_name db_hash cursor_id rows has_more =
let open Sexplib.Sexp in
let row_sexps = List.map tuple_to_sexp rows in
let rows_sexp = List row_sexps in
- to_string @@ List [
- Atom "cursor";
- List [Atom "id"; Atom cursor_id];
- List [Atom "rows"; rows_sexp];
- List [Atom "row_count"; Atom (string_of_int (List.length row_sexps))];
- List [Atom "has_more"; Atom (string_of_bool has_more)];
- List [Atom "db_hash"; Atom (short_hash db_hash)];
- List [Atom "db_name"; Atom db_name];
- List [Atom "branch"; Atom (get_branch storage)];
- ]
+ to_string
+ @@ List
+ [
+ Atom "cursor";
+ List [ Atom "id"; Atom cursor_id ];
+ List [ Atom "rows"; rows_sexp ];
+ List [ Atom "row_count"; Atom (string_of_int (List.length row_sexps)) ];
+ List [ Atom "has_more"; Atom (string_of_bool has_more) ];
+ List [ Atom "db_hash"; Atom (short_hash db_hash) ];
+ List [ Atom "db_name"; Atom db_name ];
+ List [ Atom "branch"; Atom (get_branch storage) ];
+ ]
let execute_command storage db_ref cmd =
- let db = !db_ref in
- let h = db.Management.Database.hash in
+ let db = !db_ref in
+ let h = db.Management.Database.hash in
let name = db.Management.Database.name in
- let ok = ok_response storage name in
- let err = error_response storage name in
+ let ok = ok_response storage name in
+ let err = error_response storage name in
match Dispatch.execute dbms_dispatch storage db cmd with
| Ok (Sublanguage.Query rel) ->
- relation_to_sexp storage name h rel default_limit
+ relation_to_sexp storage name h rel default_limit
| Ok (Sublanguage.Transition (new_db, msg)) ->
- db_ref := new_db;
- advance_head_branch storage new_db.Management.Database.hash;
- ok new_db.Management.Database.hash msg
+ db_ref := new_db;
+ advance_head_branch storage new_db.Management.Database.hash;
+ ok new_db.Management.Database.hash msg
| Ok (Sublanguage.Cursor { cursor_id; rows; has_more }) ->
- cursor_to_sexp storage name h cursor_id rows has_more
- | Error e ->
- err h (Dispatch.sexp_of_dispatch_error e)
+ cursor_to_sexp storage name h cursor_id rows has_more
+ | Error e -> err h (Dispatch.sexp_of_dispatch_error e)
(* TODO: registration failures are silently ignored; a broken prelude
relation leaves the catalog in a partially-seeded state with no
@@ -142,29 +153,30 @@ let register_prelude_relations storage db =
let open Prelude.Standard in
List.fold_left
(fun db (rel : Relation.t) ->
- match Manipulation.Memory.create_immutable_relation storage db
- ~name:rel.name
- ~schema:rel.schema
- ~generator:(Option.get rel.generator)
- ~membership_criteria:rel.membership_criteria
- ~cardinality:rel.cardinality
+ match
+ Manipulation.Memory.create_immutable_relation storage db ~name:rel.name
+ ~schema:rel.schema ~generator:(Option.get rel.generator)
+ ~membership_criteria:rel.membership_criteria
+ ~cardinality:rel.cardinality
with
| Ok (new_db, _) -> new_db
- | Error e ->
- Printf.eprintf "Warning: failed to register prelude relation %s: %s\n%!"
- rel.name (Manipulation.string_of_error e);
- db)
+ | Error e ->
+ Printf.eprintf
+ "Warning: failed to register prelude relation %s: %s\n%!" rel.name
+ (Manipulation.string_of_error e);
+ db)
db
- [ less_than_natural
- ; less_than_or_equal_natural
- ; greater_than_natural
- ; greater_than_or_equal_natural
- ; equal_natural
- ; not_equal_natural
- ; plus_natural
- ; times_natural
- ; minus_natural
- ; divide_natural
+ [
+ less_than_natural;
+ less_than_or_equal_natural;
+ greater_than_natural;
+ greater_than_or_equal_natural;
+ equal_natural;
+ not_equal_natural;
+ plus_natural;
+ times_natural;
+ minus_natural;
+ divide_natural;
]
let print_with_time str =
@@ -173,9 +185,9 @@ let print_with_time str =
let orange = "\027[38;5;208m" in
let reset = "\027[0m" in
let formatted_time =
- Printf.sprintf "%04d-%02d-%02d %02d:%02d:%02d"
- (tm.Unix.tm_year + 1900) (tm.Unix.tm_mon + 1) tm.Unix.tm_mday
- tm.Unix.tm_hour tm.Unix.tm_min tm.Unix.tm_sec
+ Printf.sprintf "%04d-%02d-%02d %02d:%02d:%02d" (tm.Unix.tm_year + 1900)
+ (tm.Unix.tm_mon + 1) tm.Unix.tm_mday tm.Unix.tm_hour tm.Unix.tm_min
+ tm.Unix.tm_sec
in
print_endline @@ Printf.sprintf "%s[%s]%s %s" orange formatted_time reset str
@@ -184,25 +196,26 @@ let print_with_time str =
let handle_client storage db_ref fd =
let ic = Unix.in_channel_of_descr fd in
let oc = Unix.out_channel_of_descr fd in
- (try
- while true do
- let line = input_line ic in
- let line = String.trim line in
- if line <> "" then begin
- let response =
- try execute_command storage db_ref line
- with e ->
- let db = !db_ref in
- error_response storage db.Management.Database.name
- db.Management.Database.hash
- Sexplib.Sexp.(List [Atom "uncaught-error"; Atom (Printexc.to_string e)])
- in
- print_with_time response;
- output_string oc (response ^ "\n");
- flush oc
- end
- done
- with End_of_file | Unix.Unix_error _ -> ())
+ try
+ while true do
+ let line = input_line ic in
+ let line = String.trim line in
+ if line <> "" then begin
+ let response =
+ try execute_command storage db_ref line
+ with e ->
+ let db = !db_ref in
+ error_response storage db.Management.Database.name
+ db.Management.Database.hash
+ Sexplib.Sexp.(
+ List [ Atom "uncaught-error"; Atom (Printexc.to_string e) ])
+ in
+ print_with_time response;
+ output_string oc (response ^ "\n");
+ flush oc
+ end
+ done
+ with End_of_file | Unix.Unix_error _ -> ()
let setup () =
let ( let* ) = Result.bind in
@@ -220,62 +233,62 @@ let register_branch_relations storage db =
(* Register ephemeral sakura:branch and sakura:head relations backed by
the raw branch registry — no stored tuples, no circularity. *)
let register db rel =
- match Manipulation.Memory.create_immutable_relation storage db
- ~name:rel.Relation.name
- ~schema:rel.Relation.schema
- ~generator:(Option.get rel.Relation.generator)
- ~membership_criteria:rel.Relation.membership_criteria
- ~cardinality:rel.Relation.cardinality
+ match
+ Manipulation.Memory.create_immutable_relation storage db
+ ~name:rel.Relation.name ~schema:rel.Relation.schema
+ ~generator:(Option.get rel.Relation.generator)
+ ~membership_criteria:rel.Relation.membership_criteria
+ ~cardinality:rel.Relation.cardinality
with
| Ok (db, _) -> db
| Error e ->
- Printf.eprintf "Warning: failed to register %s: %s\n%!"
- rel.Relation.name (Manipulation.string_of_error e);
- db
+ Printf.eprintf "Warning: failed to register %s: %s\n%!"
+ rel.Relation.name
+ (Manipulation.string_of_error e);
+ db
in
db
|> Fun.flip register (BranchOps.branch_relation storage)
|> Fun.flip register (BranchOps.head_relation storage)
-let warn_on_error fmt = function
- | Ok () -> ()
- | Error e -> Printf.eprintf fmt e
-
-let cursor_gc_max_age = 300.0 (* 5 minutes *)
-let cursor_gc_interval = 60.0 (* run GC at most once per minute *)
+let warn_on_error fmt = function Ok () -> () | Error e -> Printf.eprintf fmt e
+let cursor_gc_max_age = 300.0 (* 5 minutes *)
+let cursor_gc_interval = 60.0 (* run GC at most once per minute *)
let () =
match setup () with
| Error msg -> failwith msg
| Ok (storage, db) ->
- let db = db
- |> register_prelude_relations storage
- |> register_branch_relations storage
- in
- (* Initialise cursor session registry for SCL *)
- let session_reg = Session.create () in
- Scl.Executor.set_sessions session_reg;
- (* Create default master branch in raw registry and set HEAD *)
- BranchOps.create storage ~name:"master" ~tip:db.Management.Database.hash
- |> warn_on_error "Warning: failed to create master branch: %s\n%!";
- BranchOps.checkout storage "master"
- |> warn_on_error "Warning: failed to checkout master: %s\n%!";
- let db_ref = ref db in
- let last_gc = ref 0.0 in
- let sock = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
- Unix.setsockopt sock Unix.SO_REUSEADDR true;
- Unix.setsockopt sock Unix.SO_REUSEPORT true;
- Unix.bind sock (Unix.ADDR_INET (Unix.inet_addr_loopback, default_port));
- Unix.listen sock 5;
- Printf.printf "Sakura server listening on 127.0.0.1:%d\n" default_port;
- Printf.printf "Database hash: %s\n%!" (short_hash db.Management.Database.hash);
- while true do
- let (client_fd, _addr) = Unix.accept sock in
- handle_client storage db_ref client_fd;
- (try Unix.close client_fd with _ -> ());
- let now = Unix.gettimeofday () in
- if now -. !last_gc >= cursor_gc_interval then begin
- Session.gc session_reg ~max_age:cursor_gc_max_age;
- last_gc := now
- end
- done
+ let db =
+ db
+ |> register_prelude_relations storage
+ |> register_branch_relations storage
+ in
+ (* Initialise cursor session registry for SCL *)
+ let session_reg = Session.create () in
+ Scl.Executor.set_sessions session_reg;
+ (* Create default master branch in raw registry and set HEAD *)
+ BranchOps.create storage ~name:"master" ~tip:db.Management.Database.hash
+ |> warn_on_error "Warning: failed to create master branch: %s\n%!";
+ BranchOps.checkout storage "master"
+ |> warn_on_error "Warning: failed to checkout master: %s\n%!";
+ let db_ref = ref db in
+ let last_gc = ref 0.0 in
+ let sock = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
+ Unix.setsockopt sock Unix.SO_REUSEADDR true;
+ Unix.setsockopt sock Unix.SO_REUSEPORT true;
+ Unix.bind sock (Unix.ADDR_INET (Unix.inet_addr_loopback, default_port));
+ Unix.listen sock 5;
+ Printf.printf "Sakura server listening on 127.0.0.1:%d\n" default_port;
+ Printf.printf "Database hash: %s\n%!"
+ (short_hash db.Management.Database.hash);
+ while true do
+ let client_fd, _addr = Unix.accept sock in
+ handle_client storage db_ref client_fd;
+ (try Unix.close client_fd with _ -> ());
+ let now = Unix.gettimeofday () in
+ if now -. !last_gc >= cursor_gc_interval then begin
+ Session.gc session_reg ~max_age:cursor_gc_max_age;
+ last_gc := now
+ end
+ done