Merge lp:~perconardba/mydumper/0.5.2_lesslocking into lp:mydumper/0.5

Proposed by Max Bubenick
Status: Needs review
Proposed branch: lp:~perconardba/mydumper/0.5.2_lesslocking
Merge into: lp:mydumper/0.5
Diff against target: 434 lines (+157/-41)
6 files modified
.bzrignore (+1/-0)
CMakeLists.txt (+1/-1)
docs/files.rst (+3/-2)
docs/mydumper_usage.rst (+18/-0)
mydumper.c (+131/-37)
myloader.c (+3/-1)
To merge this branch: bzr merge lp:~perconardba/mydumper/0.5.2_lesslocking
Reviewer Review Type Date Requested Status
MySQL Data Dumper Team Pending
Review via email: mp+168997@code.launchpad.net

Description of the change

what we did was to add a new connection (lock_conn) that locks all non-InnoDB tables after getting metadata and then releasing the FTWRL. In this way we can release the locks on InnoDB tables earlier.

To post a comment you must log in.
Revision history for this message
David Busby (d-busby) wrote :

This merge also include patch proposed here: https://bugs.launchpad.net/mydumper/+bug/1125997 for the addition of utc-tz

110. By Max Bubenick

Added .partial to metadata, removed when finished

111. By Max Bubenick

no-lock issue fixed

112. By Max Bubenick

MyISAM lock tables fail message lvl changed, and better description

113. By Max Bubenick

ensure satetement is not bigger than statement_size

114. By Max Bubenick

less loccking is optional now, default is disabled

115. By Max Bubenick

fix #1124106 at myloader

116. By Max Bubenick

documentation added

117. By Max Bubenick

bug fix #987344 #1075611

118. By Max Bubenick

small fixes

119. By Max Bubenick

--success-on-1146 added

Unmerged revisions

119. By Max Bubenick

--success-on-1146 added

118. By Max Bubenick

small fixes

117. By Max Bubenick

bug fix #987344 #1075611

116. By Max Bubenick

documentation added

115. By Max Bubenick

fix #1124106 at myloader

114. By Max Bubenick

less loccking is optional now, default is disabled

113. By Max Bubenick

ensure satetement is not bigger than statement_size

112. By Max Bubenick

MyISAM lock tables fail message lvl changed, and better description

111. By Max Bubenick

no-lock issue fixed

110. By Max Bubenick

Added .partial to metadata, removed when finished

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2011-05-16 12:12:33 +0000
3+++ .bzrignore 2013-10-21 13:19:20 +0000
4@@ -17,3 +17,4 @@
5 docs/man/
6 docs/_build/conf.py
7 docs/_build/sources.cmake
8+.project
9
10=== modified file 'CMakeLists.txt'
11--- CMakeLists.txt 2012-11-19 17:38:52 +0000
12+++ CMakeLists.txt 2013-10-21 13:19:20 +0000
13@@ -1,6 +1,6 @@
14 cmake_minimum_required(VERSION 2.6)
15 project(mydumper)
16-set(VERSION 0.5.2)
17+set(VERSION 0.5.3)
18 set(ARCHIVE_NAME "${CMAKE_PROJECT_NAME}-${VERSION}")
19
20 #Required packages
21
22=== modified file 'docs/files.rst'
23--- docs/files.rst 2011-06-14 09:17:12 +0000
24+++ docs/files.rst 2013-10-21 13:19:20 +0000
25@@ -6,8 +6,9 @@
26
27 Metadata
28 --------
29-When a dump is executed a file called ``.metadata`` is created in the output
30-directory. This contains the start and end time of the dump as well as the
31+When a dump is executed a file called ``metadata.partial`` is created in the output
32+directory and is renamed to ``metadata`` when mydumper finish without error.
33+This contains the start and end time of the dump as well as the
34 master binary log positions if applicable.
35
36 This is an example of the content of this file::
37
38=== modified file 'docs/mydumper_usage.rst'
39--- docs/mydumper_usage.rst 2011-06-20 06:32:35 +0000
40+++ docs/mydumper_usage.rst 2013-10-21 13:19:20 +0000
41@@ -151,3 +151,21 @@
42 .. warning::
43
44 This will cause inconsistent backups.
45+
46+.. option:: --[skip-]tz-utc
47+
48+ SET TIME_ZONE='+00:00' at top of dump to allow dumping of TIMESTAMP data
49+ when a server has data in different time zones or data is being moved
50+ between servers with different time zones, defaults to on use --skip-tz-utc
51+ to disable.
52+
53+.. option:: --less-locking
54+
55+ Minimize locking time on InnoDB tables grabbing a LOCK TABLE ... READ
56+ on all non-innodb tables.
57+
58+ .. warning::
59+
60+ Do not use this option if you are UPDATING or DELETING MyISAM or Memory
61+ tables. This could cause mutual locks between mydumper and application
62+ threads.
63
64=== modified file 'mydumper.c'
65--- mydumper.c 2012-10-20 17:40:00 +0000
66+++ mydumper.c 2013-10-21 13:19:20 +0000
67@@ -53,7 +53,7 @@
68 guint rows_per_file= 0;
69 int longquery= 60;
70 int build_empty_files= 0;
71-
72+int skip_tz= 0;
73 int need_dummy_read= 0;
74 int compress_output= 0;
75 int killqueries= 0;
76@@ -76,9 +76,12 @@
77
78 gboolean no_schemas= FALSE;
79 gboolean no_locks= FALSE;
80+gboolean less_locking = FALSE;
81+gboolean success_on_1146 = FALSE;
82
83 GList *innodb_tables= NULL;
84 GList *non_innodb_table= NULL;
85+GList *non_innodb_table_block= NULL;
86 GList *table_schemas= NULL;
87 gint non_innodb_table_counter= 0;
88 gint non_innodb_done= 0;
89@@ -105,12 +108,16 @@
90 { "ignore-engines", 'i', 0, G_OPTION_ARG_STRING, &ignore_engines, "Comma delimited list of storage engines to ignore", NULL },
91 { "no-schemas", 'm', 0, G_OPTION_ARG_NONE, &no_schemas, "Do not dump table schemas with the data", NULL },
92 { "no-locks", 'k', 0, G_OPTION_ARG_NONE, &no_locks, "Do not execute the temporary shared read lock. WARNING: This will cause inconsistent backups", NULL },
93+ { "less-locking", 0, 0, G_OPTION_ARG_NONE, &less_locking, "Minimize locking time on InnoDB tables, WARNING: be carefull, please READ documentation first", NULL},
94 { "long-query-guard", 'l', 0, G_OPTION_ARG_INT, &longquery, "Set long query timer in seconds, default 60", NULL },
95 { "kill-long-queries", 'k', 0, G_OPTION_ARG_NONE, &killqueries, "Kill long running queries (instead of aborting)", NULL },
96 { "binlogs", 'b', 0, G_OPTION_ARG_NONE, &need_binlogs, "Get a snapshot of the binary logs as well as dump data", NULL },
97 { "daemon", 'D', 0, G_OPTION_ARG_NONE, &daemon_mode, "Enable daemon mode", NULL },
98 { "snapshot-interval", 'I', 0, G_OPTION_ARG_INT, &snapshot_interval, "Interval between each dump snapshot (in minutes), requires --daemon, default 60", NULL },
99 { "logfile", 'L', 0, G_OPTION_ARG_FILENAME, &logfile, "Log file name to use, by default stdout is used", NULL },
100+ { "tz-utc", 0, 0, G_OPTION_ARG_NONE, NULL, "SET TIME_ZONE='+00:00' at top of dump to allow dumping of TIMESTAMP data when a server has data in different time zones or data is being moved between servers with different time zones, defaults to on use --skip-tz-utc to disable.", NULL },
101+ { "skip-tz-utc", 0, 0, G_OPTION_ARG_NONE, &skip_tz, "", NULL },
102+ { "success-on-1146", 0, 0, G_OPTION_ARG_NONE, &success_on_1146, "Not increment error count and Warning instead of Critical in case of table doesn't exist", NULL},
103 { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL }
104 };
105
106@@ -130,7 +137,7 @@
107 void no_log(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data);
108 void set_verbose(guint verbosity);
109 MYSQL *reconnect_for_binlog(MYSQL *thrconn);
110-void start_dump(MYSQL *conn);
111+void start_dump(MYSQL *conn, MYSQL *lock_conn);
112 MYSQL *create_main_connection();
113 void *binlog_thread(void *data);
114 void *exec_thread(void *data);
115@@ -330,6 +337,10 @@
116 g_critical("Failed to start consistent snapshot: %s",mysql_error(thrconn));
117 errors++;
118 }
119+ if(!skip_tz && mysql_query(thrconn, "/*!40103 SET TIME_ZONE='+00:00' */")){
120+ g_critical("Failed to set time zone: %s",mysql_error(thrconn));
121+ }
122+
123 /* Unfortunately version before 4.1.8 did not support consistent snapshot transaction starts, so we cheat */
124 if (need_dummy_read) {
125 mysql_query(thrconn,"SELECT /*!40001 SQL_NO_CACHE */ * FROM mysql.mydumperdummy");
126@@ -553,8 +564,10 @@
127 g_source_remove(sigsource);
128 } else {
129 MYSQL *conn= create_main_connection();
130- start_dump(conn);
131+ MYSQL *lock_conn= create_main_connection();
132+ start_dump(conn, lock_conn);
133 mysql_close(conn);
134+ mysql_close(lock_conn);
135 }
136
137 sleep(5);
138@@ -612,8 +625,10 @@
139 g_async_queue_pop(start_scheduled_dump);
140 clear_dump_directory();
141 MYSQL *conn= create_main_connection();
142- start_dump(conn);
143+ MYSQL *lock_conn= create_main_connection();
144+ start_dump(conn,lock_conn);
145 mysql_close(conn);
146+ mysql_close(lock_conn);
147 mysql_thread_end();
148
149 // Don't switch the symlink on shutdown because the dump is probably incomplete.
150@@ -671,19 +686,23 @@
151 return NULL;
152 }
153
154-void start_dump(MYSQL *conn)
155+void start_dump(MYSQL *conn, MYSQL *lock_conn)
156 {
157 struct configuration conf = { 1, NULL, NULL, NULL, NULL, 0 };
158 char *p;
159+ char *p2;
160+ char *p3;
161 time_t t;
162+ struct db_table *dbt;
163+ int main_lock = 1;
164
165 if (daemon_mode)
166- p= g_strdup_printf("%s/%d/metadata", output_directory, dump_number);
167+ p= g_strdup_printf("%s/%d/metadata.partial", output_directory, dump_number);
168 else
169- p= g_strdup_printf("%s/metadata", output_directory);
170+ p= g_strdup_printf("%s/metadata.partial", output_directory);
171+ p2 = g_strndup(p, (unsigned)strlen(p)-8);
172
173 FILE* mdfile=g_fopen(p,"w");
174- g_free(p);
175 if(!mdfile) {
176 g_critical("Couldn't write metadata file (%d)",errno);
177 exit(EXIT_FAILURE);
178@@ -718,11 +737,11 @@
179 continue;
180 if (row[tcol] && atoi(row[tcol])>longquery) {
181 if (killqueries) {
182- if (mysql_query(conn,p=g_strdup_printf("KILL %lu",atol(row[icol]))))
183+ if (mysql_query(conn,p3=g_strdup_printf("KILL %lu",atol(row[icol]))))
184 g_warning("Could not KILL slow query: %s",mysql_error(conn));
185 else
186 g_warning("Killed a query that was running for %ss",row[tcol]);
187- g_free(p);
188+ g_free(p3);
189 } else {
190 g_critical("There are queries in PROCESSLIST running longer than %us, aborting dump,\n\t"
191 "use --long-query-guard to change the guard value, kill queries (--kill-long-queries) or use \n\tdifferent server for dump", longquery);
192@@ -800,9 +819,43 @@
193 mysql_free_result(databases);
194
195 }
196- struct db_table *dbt;
197- if (!non_innodb_table) {
198- g_async_queue_push(conf.unlock_tables, GINT_TO_POINTER(1));
199+
200+ if (!no_locks && less_locking) {
201+ GString *query = g_string_sized_new(1024);
202+ gchar *dt = NULL;
203+
204+ if (!non_innodb_table) {
205+ g_async_queue_push(conf.unlock_tables, GINT_TO_POINTER(1));
206+ }else{
207+ int first = 1;
208+ /* Create a read lock for non_innodb_table */
209+ non_innodb_table_block = non_innodb_table;
210+ for (non_innodb_table_block= g_list_first(non_innodb_table_block); non_innodb_table_block; non_innodb_table_block= g_list_next(non_innodb_table_block)) {
211+ dbt= (struct db_table*) non_innodb_table_block->data;
212+ if(!(strcasecmp(dbt->database,"mysql") == 0 && (strcasecmp(dbt->table,"general_log") ==0 || strcasecmp(dbt->table,"slow_log") ==0 ))){
213+ dt = g_strjoin(".",dbt->database,dbt->table,NULL);
214+ if(first){
215+ g_string_printf(query, "LOCK TABLES ");
216+ first = 0;
217+ }else{
218+ g_string_append(query, ", ");
219+ }
220+ g_string_append(query, dt);
221+ g_string_append(query, " READ");
222+ }
223+ }
224+ g_free(dt);
225+
226+ /* if the LT fails do not realease the FTWRL */
227+ if(mysql_query(lock_conn, query->str)){
228+ g_warning("MyISAM lock tables fail: %s", mysql_error(lock_conn));
229+ }else{
230+ g_message("Unlocking Innodb tables");
231+ mysql_query(conn, "UNLOCK TABLES");
232+ main_lock = 0;
233+ }
234+ }
235+ g_string_free(query, TRUE);
236 }
237
238 for (non_innodb_table= g_list_first(non_innodb_table); non_innodb_table; non_innodb_table= g_list_next(non_innodb_table)) {
239@@ -833,17 +886,20 @@
240 get_binlogs(conn, &conf);
241 }
242
243-
244 for (n=0; n<num_threads; n++) {
245 struct job *j = g_new0(struct job,1);
246 j->type = JOB_SHUTDOWN;
247 g_async_queue_push(conf.queue,j);
248 }
249
250- g_async_queue_pop(conf.unlock_tables);
251 if (!no_locks) {
252- g_message("Non-InnoDB dump complete, unlocking tables");
253- mysql_query(conn, "UNLOCK TABLES");
254+ g_async_queue_pop(conf.unlock_tables);
255+ if(less_locking) {
256+ mysql_query(lock_conn, "UNLOCK TABLES /* Non Innodb */");
257+ g_message("Non-InnoDB dump complete, unlocking tables");
258+ }
259+ if(main_lock)
260+ mysql_query(conn, "UNLOCK TABLES");
261 }
262
263 for (n=0; n<num_threads; n++) {
264@@ -856,6 +912,9 @@
265 tval.tm_year+1900, tval.tm_mon+1, tval.tm_mday,
266 tval.tm_hour, tval.tm_min, tval.tm_sec);
267 fclose(mdfile);
268+ g_rename(p, p2);
269+ g_free(p);
270+ g_free(p2);
271 g_message("Finished dump at: %04d-%02d-%02d %02d:%02d:%02d\n",
272 tval.tm_year+1900, tval.tm_mon+1, tval.tm_mday,
273 tval.tm_hour, tval.tm_min, tval.tm_sec);
274@@ -954,8 +1013,10 @@
275 estimated_step = (nmax-nmin)/estimated_chunks+1;
276 cutoff = nmin;
277 while(cutoff<=nmax) {
278- chunks=g_list_append(chunks,g_strdup_printf("%s%s(`%s` >= %llu AND `%s` < %llu)",
279+ chunks=g_list_append(chunks,g_strdup_printf("%s%s%s%s(`%s` >= %llu AND `%s` < %llu)",
280+ !showed_nulls?"`":"",
281 !showed_nulls?field:"",
282+ !showed_nulls?"`":"",
283 !showed_nulls?" IS NULL OR ":"",
284 field, (unsigned long long)cutoff,
285 field, (unsigned long long)(cutoff+estimated_step)));
286@@ -1180,9 +1241,13 @@
287
288 query= g_strdup_printf("SHOW CREATE TABLE `%s`.`%s`", database, table);
289 if (mysql_query(conn, query) || !(result= mysql_use_result(conn))) {
290- g_critical("Error dumping schemas (%s.%s): %s", database, table, mysql_error(conn));
291+ if(success_on_1146 && mysql_errno(conn) == 1146){
292+ g_warning("Error dumping schemas (%s.%s): %s", database, table, mysql_error(conn));
293+ }else{
294+ g_critical("Error dumping schemas (%s.%s): %s", database, table, mysql_error(conn));
295+ errors++;
296+ }
297 g_free(query);
298- errors++;
299 return;
300 }
301
302@@ -1306,15 +1371,20 @@
303 guint i;
304 guint num_fields = 0;
305 guint64 num_rows = 0;
306+ guint64 num_rows_st = 0;
307 MYSQL_RES *result = NULL;
308 char *query = NULL;
309
310 /* Ghm, not sure if this should be statement_size - but default isn't too big for now */
311 GString* statement = g_string_sized_new(statement_size);
312-
313+ GString* statement_row = g_string_sized_new(0);
314+
315 if (detected_server == SERVER_TYPE_MYSQL) {
316 g_string_printf(statement,"/*!40101 SET NAMES binary*/;\n");
317 g_string_append(statement,"/*!40014 SET FOREIGN_KEY_CHECKS=0*/;\n");
318+ if (!skip_tz) {
319+ g_string_append(statement,"/*!40103 SET TIME_ZONE='+00:00' */;\n");
320+ }
321 } else {
322 g_string_printf(statement,"SET FOREIGN_KEY_CHECKS=0;\n");
323 }
324@@ -1327,9 +1397,14 @@
325 /* Poor man's database code */
326 query = g_strdup_printf("SELECT %s * FROM `%s`.`%s` %s %s", (detected_server == SERVER_TYPE_MYSQL) ? "/*!40001 SQL_NO_CACHE */" : "", database, table, where?"WHERE":"",where?where:"");
327 if (mysql_query(conn, query) || !(result=mysql_use_result(conn))) {
328- g_critical("Error dumping table (%s.%s) data: %s ",database, table, mysql_error(conn));
329+ //ERROR 1146
330+ if(success_on_1146 && mysql_errno(conn) == 1146){
331+ g_warning("Error dumping table (%s.%s) data: %s ",database, table, mysql_error(conn));
332+ }else{
333+ g_critical("Error dumping table (%s.%s) data: %s ",database, table, mysql_error(conn));
334+ errors++;
335+ }
336 g_free(query);
337- errors++;
338 return num_rows;
339 }
340
341@@ -1348,38 +1423,57 @@
342 gulong *lengths = mysql_fetch_lengths(result);
343 num_rows++;
344
345- if (!statement->len)
346- g_string_printf(statement, "INSERT INTO `%s` VALUES\n(", table);
347- else
348- g_string_append(statement, ",\n(");
349+ if (!statement->len){
350+ g_string_printf(statement, "INSERT INTO `%s` VALUES", table);
351+ num_rows_st = 0;
352+ }
353+
354+ if (statement_row->len) {
355+ g_string_append(statement, statement_row->str);
356+ g_string_set_size(statement_row,0);
357+ num_rows_st++;
358+ }
359+
360+ g_string_append(statement_row, "\n(");
361
362 for (i = 0; i < num_fields; i++) {
363 /* Don't escape safe formats, saves some time */
364 if (!row[i]) {
365- g_string_append(statement, "NULL");
366+ g_string_append(statement_row, "NULL");
367 } else if (fields[i].flags & NUM_FLAG) {
368- g_string_append(statement, row[i]);
369+ g_string_append(statement_row, row[i]);
370 } else {
371 /* We reuse buffers for string escaping, growing is expensive just at the beginning */
372 g_string_set_size(escaped, lengths[i]*2+1);
373 mysql_real_escape_string(conn, escaped->str, row[i], lengths[i]);
374- g_string_append_c(statement,'\"');
375- g_string_append(statement,escaped->str);
376- g_string_append_c(statement,'\"');
377+ g_string_append_c(statement_row,'\"');
378+ g_string_append(statement_row,escaped->str);
379+ g_string_append_c(statement_row,'\"');
380 }
381 if (i < num_fields - 1) {
382- g_string_append_c(statement,',');
383+ g_string_append_c(statement_row,',');
384 } else {
385- /* INSERT statement is closed once over limit */
386- if (statement->len > statement_size) {
387- g_string_append(statement,");\n");
388+ g_string_append_c(statement_row,')');
389+ /* INSERT statement is closed before over limit */
390+ if(statement->len+statement_row->len+1 > statement_size) {
391+ if(num_rows_st == 0){
392+ g_string_append(statement, statement_row->str);
393+ g_string_set_size(statement_row,0);
394+ g_warning("Row bigger than statement_size for %s.%s", database, table);
395+ }
396+ g_string_append(statement,";\n");
397+
398 if (!write_data(file,statement)) {
399 g_critical("Could not write out data for %s.%s", database, table);
400 goto cleanup;
401 }
402 g_string_set_size(statement,0);
403 } else {
404- g_string_append_c(statement,')');
405+ if(num_rows > 1)
406+ g_string_append(statement,",");
407+ g_string_append(statement, statement_row->str);
408+ num_rows_st++;
409+ g_string_set_size(statement_row,0);
410 }
411 }
412 }
413
414=== modified file 'myloader.c'
415--- myloader.c 2012-10-20 17:40:00 +0000
416+++ myloader.c 2013-10-21 13:19:20 +0000
417@@ -207,7 +207,7 @@
418 gchar** split_file= g_strsplit(filename, ".", 0);
419 gchar* database= split_file[0];
420 // Remove the -schema from the table name
421- gchar** split_table= g_strsplit(split_file[1], "-", 0);
422+ gchar** split_table= g_strsplit(split_file[1], "-schema", 0);
423 gchar* table= split_table[0];
424
425 gchar* query= g_strdup_printf("SHOW CREATE DATABASE `%s`", db ? db : database);
426@@ -279,6 +279,8 @@
427 mysql_query(thrconn, "SET SQL_LOG_BIN=0");
428
429 mysql_query(thrconn, "/*!40101 SET NAMES binary*/");
430+ mysql_query(thrconn, "/*!40101 SET SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */");
431+ mysql_query(thrconn, "/*!40014 SET UNIQUE_CHECKS=0 */");
432 mysql_query(thrconn, "SET autocommit=0");
433
434 g_async_queue_push(conf->ready, GINT_TO_POINTER(1));

Subscribers

People subscribed via source and target branches