1 /*-
2 * Copyright (c) 2014-2018 MongoDB, Inc.
3 * Copyright (c) 2008-2014 WiredTiger, Inc.
4 * All rights reserved.
5 *
6 * See the file LICENSE for redistribution information.
7 */
8
9 #include "wt_internal.h"
10
11 static int __ckpt_server_start(WT_CONNECTION_IMPL *);
12
13 /*
14 * __ckpt_server_config --
15 * Parse and setup the checkpoint server options.
16 */
17 static int
__ckpt_server_config(WT_SESSION_IMPL * session,const char ** cfg,bool * startp)18 __ckpt_server_config(WT_SESSION_IMPL *session, const char **cfg, bool *startp)
19 {
20 WT_CONFIG_ITEM cval;
21 WT_CONNECTION_IMPL *conn;
22
23 *startp = false;
24
25 conn = S2C(session);
26
27 WT_RET(__wt_config_gets(session, cfg, "checkpoint.wait", &cval));
28 conn->ckpt_usecs = (uint64_t)cval.val * WT_MILLION;
29
30 WT_RET(__wt_config_gets(session, cfg, "checkpoint.log_size", &cval));
31 conn->ckpt_logsize = (wt_off_t)cval.val;
32
33 /*
34 * The checkpoint configuration requires a wait time and/or a log size,
35 * if neither is set, we're not running at all. Checkpoints based on log
36 * size also require logging be enabled.
37 */
38 if (conn->ckpt_usecs != 0 ||
39 (conn->ckpt_logsize != 0 &&
40 FLD_ISSET(conn->log_flags, WT_CONN_LOG_ENABLED))) {
41 /*
42 * If checkpointing based on log data, use a minimum of the
43 * log file size. The logging subsystem has already been
44 * initialized.
45 */
46 if (conn->ckpt_logsize != 0 &&
47 FLD_ISSET(conn->log_flags, WT_CONN_LOG_ENABLED))
48 conn->ckpt_logsize = WT_MAX(
49 conn->ckpt_logsize, conn->log_file_max);
50 /* Checkpoints are incompatible with in-memory configuration */
51 WT_RET(__wt_config_gets(session, cfg, "in_memory", &cval));
52 if (cval.val != 0)
53 WT_RET_MSG(session, EINVAL,
54 "checkpoint configuration incompatible with "
55 "in-memory configuration");
56
57 __wt_log_written_reset(session);
58
59 *startp = true;
60 }
61
62 return (0);
63 }
64
65 /*
66 * __ckpt_server_run_chk --
67 * Check to decide if the checkpoint server should continue running.
68 */
69 static bool
__ckpt_server_run_chk(WT_SESSION_IMPL * session)70 __ckpt_server_run_chk(WT_SESSION_IMPL *session)
71 {
72 return (F_ISSET(S2C(session), WT_CONN_SERVER_CHECKPOINT));
73 }
74
75 /*
76 * __ckpt_server --
77 * The checkpoint server thread.
78 */
79 static WT_THREAD_RET
__ckpt_server(void * arg)80 __ckpt_server(void *arg)
81 {
82 WT_CONNECTION_IMPL *conn;
83 WT_DECL_RET;
84 WT_SESSION *wt_session;
85 WT_SESSION_IMPL *session;
86 uint64_t checkpoint_gen;
87
88 session = arg;
89 conn = S2C(session);
90 wt_session = (WT_SESSION *)session;
91
92 for (;;) {
93 /*
94 * Wait...
95 * NOTE: If the user only configured logsize, then usecs
96 * will be 0 and this wait won't return until signalled.
97 */
98 __wt_cond_wait(session,
99 conn->ckpt_cond, conn->ckpt_usecs, __ckpt_server_run_chk);
100
101 /* Check if we're quitting or being reconfigured. */
102 if (!__ckpt_server_run_chk(session))
103 break;
104
105 checkpoint_gen = __wt_gen(session, WT_GEN_CHECKPOINT);
106 WT_ERR(wt_session->checkpoint(wt_session, NULL));
107
108 /*
109 * Reset the log file size counters if the checkpoint wasn't
110 * skipped.
111 */
112 if (checkpoint_gen != __wt_gen(session, WT_GEN_CHECKPOINT) &&
113 conn->ckpt_logsize) {
114 __wt_log_written_reset(session);
115 conn->ckpt_signalled = false;
116
117 /*
118 * In case we crossed the log limit during the
119 * checkpoint and the condition variable was
120 * already signalled, do a tiny wait to clear
121 * it so we don't do another checkpoint
122 * immediately.
123 */
124 __wt_cond_wait(session, conn->ckpt_cond, 1, NULL);
125 }
126 }
127
128 if (0) {
129 err: WT_PANIC_MSG(session, ret, "checkpoint server error");
130 }
131 return (WT_THREAD_RET_VALUE);
132 }
133
134 /*
135 * __ckpt_server_start --
136 * Start the checkpoint server thread.
137 */
138 static int
__ckpt_server_start(WT_CONNECTION_IMPL * conn)139 __ckpt_server_start(WT_CONNECTION_IMPL *conn)
140 {
141 WT_SESSION_IMPL *session;
142 uint32_t session_flags;
143
144 /* Nothing to do if the server is already running. */
145 if (conn->ckpt_session != NULL)
146 return (0);
147
148 F_SET(conn, WT_CONN_SERVER_CHECKPOINT);
149
150 /*
151 * The checkpoint server gets its own session.
152 *
153 * Checkpoint does enough I/O it may be called upon to perform slow
154 * operations for the block manager.
155 */
156 session_flags = WT_SESSION_CAN_WAIT;
157 WT_RET(__wt_open_internal_session(conn,
158 "checkpoint-server", true, session_flags, &conn->ckpt_session));
159 session = conn->ckpt_session;
160
161 WT_RET(__wt_cond_alloc(session, "checkpoint server", &conn->ckpt_cond));
162
163 /*
164 * Start the thread.
165 */
166 WT_RET(__wt_thread_create(
167 session, &conn->ckpt_tid, __ckpt_server, session));
168 conn->ckpt_tid_set = true;
169
170 return (0);
171 }
172
173 /*
174 * __wt_checkpoint_server_create --
175 * Configure and start the checkpoint server.
176 */
177 int
__wt_checkpoint_server_create(WT_SESSION_IMPL * session,const char * cfg[])178 __wt_checkpoint_server_create(WT_SESSION_IMPL *session, const char *cfg[])
179 {
180 WT_CONNECTION_IMPL *conn;
181 bool start;
182
183 conn = S2C(session);
184 start = false;
185
186 /*
187 * Stop any server that is already running. This means that each time
188 * reconfigure is called we'll bounce the server even if there are no
189 * configuration changes. This makes our life easier as the underlying
190 * configuration routine doesn't have to worry about freeing objects
191 * in the connection structure (it's guaranteed to always start with a
192 * blank slate), and we don't have to worry about races where a running
193 * server is reading configuration information that we're updating, and
194 * it's not expected that reconfiguration will happen a lot.
195 */
196 if (conn->ckpt_session != NULL)
197 WT_RET(__wt_checkpoint_server_destroy(session));
198
199 WT_RET(__ckpt_server_config(session, cfg, &start));
200 if (start)
201 WT_RET(__ckpt_server_start(conn));
202
203 return (0);
204 }
205
206 /*
207 * __wt_checkpoint_server_destroy --
208 * Destroy the checkpoint server thread.
209 */
210 int
__wt_checkpoint_server_destroy(WT_SESSION_IMPL * session)211 __wt_checkpoint_server_destroy(WT_SESSION_IMPL *session)
212 {
213 WT_CONNECTION_IMPL *conn;
214 WT_DECL_RET;
215 WT_SESSION *wt_session;
216
217 conn = S2C(session);
218
219 F_CLR(conn, WT_CONN_SERVER_CHECKPOINT);
220 if (conn->ckpt_tid_set) {
221 __wt_cond_signal(session, conn->ckpt_cond);
222 WT_TRET(__wt_thread_join(session, &conn->ckpt_tid));
223 conn->ckpt_tid_set = false;
224 }
225 __wt_cond_destroy(session, &conn->ckpt_cond);
226
227 /* Close the server thread's session. */
228 if (conn->ckpt_session != NULL) {
229 wt_session = &conn->ckpt_session->iface;
230 WT_TRET(wt_session->close(wt_session, NULL));
231 }
232
233 /*
234 * Ensure checkpoint settings are cleared - so that reconfigure doesn't
235 * get confused.
236 */
237 conn->ckpt_session = NULL;
238 conn->ckpt_tid_set = false;
239 conn->ckpt_cond = NULL;
240 conn->ckpt_usecs = 0;
241
242 return (ret);
243 }
244
245 /*
246 * __wt_checkpoint_signal --
247 * Signal the checkpoint thread if sufficient log has been written.
248 */
249 void
__wt_checkpoint_signal(WT_SESSION_IMPL * session,wt_off_t logsize)250 __wt_checkpoint_signal(WT_SESSION_IMPL *session, wt_off_t logsize)
251 {
252 WT_CONNECTION_IMPL *conn;
253
254 conn = S2C(session);
255 WT_ASSERT(session, WT_CKPT_LOGSIZE(conn));
256 if (logsize >= conn->ckpt_logsize && !conn->ckpt_signalled) {
257 __wt_cond_signal(session, conn->ckpt_cond);
258 conn->ckpt_signalled = true;
259 }
260 }
261