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