forked from xapi-project/xen-api
-
Notifications
You must be signed in to change notification settings - Fork 0
/
db_connections.ml
110 lines (95 loc) · 4.26 KB
/
db_connections.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
(*
* Copyright (C) 2006-2009 Citrix Systems Inc.
*
* This program 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; version 2.1 only. with the special
* exception on linking described in file LICENSE.
*
* This program 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.
*)
module D = Debug.Make(struct let name = "xapi" end)
module R = Debug.Make(struct let name = "redo_log" end)
open D
let get_dbs_and_gen_counts() =
List.map (fun conn->(Parse_db_conf.generation_read conn, conn)) (Db_conn_store.read_db_connections())
(** Returns true if the supplied connection actually exists, false otherwise.
Note that, although the two files should be present (or absent) together,
after upgrade we only have a database. In this case the generation
defaults back to 0L *)
let exists connection =
Sys.file_exists connection.Parse_db_conf.path
(* This returns the most recent of the db connections to populate from. It also initialises the in-memory
generation count to the largest of the db connections' generation counts *)
let choose connections = match List.filter exists connections with
| [] -> None
| (c :: cs) as connections ->
List.iter (fun c -> debug "Dbconf contains: %s (generation %Ld)" c.Parse_db_conf.path (Parse_db_conf.generation_read c)) connections;
let gen, most_recent = List.fold_left (fun (g, c) c' ->
let g' = Parse_db_conf.generation_read c' in
if g' > g then (g', c') else (g, c))
(Parse_db_conf.generation_read c, c) cs in
debug "Most recent db is %s (generation %Ld)" most_recent.Parse_db_conf.path gen;
Some most_recent
let preferred_write_db () =
List.hd (Db_conn_store.read_db_connections()) (* !!! FIX ME *)
(* This is set by signal handlers. It instructs the db thread to call exit after the next flush *)
let exit_on_next_flush = ref false
(* db flushing thread refcount: the last thread out of the door does the exit(0) when flush_on_exit is true *)
let db_flush_thread_refcount_m = Mutex.create()
let db_flush_thread_refcount = ref 0
let inc_db_flush_thread_refcount() =
Threadext.Mutex.execute db_flush_thread_refcount_m
(fun () -> db_flush_thread_refcount := !db_flush_thread_refcount + 1)
let dec_and_read_db_flush_thread_refcount() =
Threadext.Mutex.execute db_flush_thread_refcount_m
(fun () ->
db_flush_thread_refcount := !db_flush_thread_refcount - 1;
!db_flush_thread_refcount
)
let pre_exit_hook () =
(* We're about to exit. Close the active redo logs. *)
Redo_log.with_active_redo_logs (Redo_log.shutdown);
R.debug "Closed all active redo logs."
(* The connection flushing calls each lock the connection they're flushing to.
The backend flush calls have to do enough locking (i.e. with the db_lock) to ensure that they
flush a consistent snapshot. Backends must also ensure that they do not hold the global db_lock
whilst they are writing to non-local storage.
*)
let flush_dirty_and_maybe_exit dbconn exit_spec =
Db_conn_store.with_db_conn_lock dbconn
(fun () ->
(* if we're being told to shutdown by signal handler then flush every connection
- the rationale is that we're not sure which db connections will be available on next restart *)
if !exit_on_next_flush then
begin
let (_: bool) = Backend_xml.flush_dirty dbconn in
let refcount = dec_and_read_db_flush_thread_refcount() in
(* last flushing thread close the door on the way out.. *)
if refcount = 0 then
begin
debug "refcount is 0; exiting";
pre_exit_hook();
exit 0
end
else
debug "refcount is %d; not exiting" refcount
end;
let was_anything_flushed = Backend_xml.flush_dirty dbconn in
(* exit if we've been told to by caller *)
begin
match exit_spec with
None -> ()
| (Some ret_code) -> pre_exit_hook(); exit ret_code
end;
was_anything_flushed
)
let flush dbconn db =
debug "About to flush database: %s" dbconn.Parse_db_conf.path;
Db_conn_store.with_db_conn_lock dbconn
(fun () ->
Backend_xml.flush dbconn db
)