Browse Source

add option to control amount of fsync()ing

Oswald Buddenhagen 12 years ago
parent
commit
35851f133b
8 changed files with 50 additions and 6 deletions
  1. 2 0
      NEWS
  2. 2 0
      TODO
  3. 11 0
      src/config.c
  4. 2 2
      src/drv_maildir.c
  5. 6 0
      src/isync.h
  6. 21 1
      src/mbsync.1
  7. 3 1
      src/run-tests.pl
  8. 3 2
      src/sync.c

+ 2 - 0
NEWS

@@ -10,6 +10,8 @@ UIDPLUS extension (e.g., M$ Exchange).
 
 
 More automatic handling of SSL certificates.
 More automatic handling of SSL certificates.
 
 
+Data safety in case of system crashes is improved.
+
 [1.0.0]
 [1.0.0]
 
 
 Essentially a rewrite. Synchronization state storage concept, configuration
 Essentially a rewrite. Synchronization state storage concept, configuration

+ 2 - 0
TODO

@@ -1,5 +1,7 @@
 find out why mutt's message size calc is confused.
 find out why mutt's message size calc is confused.
 
 
+f{,data}sync() usage could be optimized by batching the calls.
+
 add some marker about message being already [remotely] trashed.
 add some marker about message being already [remotely] trashed.
 real transactions would be certainly not particularly useful ...
 real transactions would be certainly not particularly useful ...
 
 

+ 11 - 0
src/config.c

@@ -39,6 +39,7 @@ channel_conf_t *channels;
 group_conf_t *groups;
 group_conf_t *groups;
 int global_ops[2];
 int global_ops[2];
 char *global_sync_state;
 char *global_sync_state;
+int FSyncLevel = FSYNC_NORMAL;
 
 
 #define ARG_OPTIONAL 0
 #define ARG_OPTIONAL 0
 #define ARG_REQUIRED 1
 #define ARG_REQUIRED 1
@@ -448,6 +449,16 @@ load_config( const char *where, int pseudo )
 			}
 			}
 			break;
 			break;
 		}
 		}
+		else if (!strcasecmp( "FSync", cfile.cmd ))
+		{
+			arg = cfile.val;
+			if (!strcasecmp( "None", arg ))
+				FSyncLevel = FSYNC_NONE;
+			else if (!strcasecmp( "Normal", arg ))
+				FSyncLevel = FSYNC_NORMAL;
+			else if (!strcasecmp( "Thorough", arg ))
+				FSyncLevel = FSYNC_THOROUGH;
+		}
 		else if (!getopt_helper( &cfile, &gcops, global_ops, &global_sync_state ))
 		else if (!getopt_helper( &cfile, &gcops, global_ops, &global_sync_state ))
 		{
 		{
 			error( "%s:%d: unknown section keyword '%s'\n",
 			error( "%s:%d: unknown section keyword '%s'\n",

+ 2 - 2
src/drv_maildir.c

@@ -432,7 +432,7 @@ maildir_store_uid( maildir_store_t *ctx )
 
 
 	n = sprintf( buf, "%d\n%d\n", ctx->gen.uidvalidity, ctx->nuid );
 	n = sprintf( buf, "%d\n%d\n", ctx->gen.uidvalidity, ctx->nuid );
 	lseek( ctx->uvfd, 0, SEEK_SET );
 	lseek( ctx->uvfd, 0, SEEK_SET );
-	if (write( ctx->uvfd, buf, n ) != n || ftruncate( ctx->uvfd, n ) || fdatasync( ctx->uvfd )) {
+	if (write( ctx->uvfd, buf, n ) != n || ftruncate( ctx->uvfd, n ) || (FSyncLevel >= FSYNC_NORMAL && fdatasync( ctx->uvfd ))) {
 		error( "Maildir error: cannot write UIDVALIDITY.\n" );
 		error( "Maildir error: cannot write UIDVALIDITY.\n" );
 		return DRV_BOX_BAD;
 		return DRV_BOX_BAD;
 	}
 	}
@@ -1204,7 +1204,7 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
 	}
 	}
 	ret = write( fd, data->data, data->len );
 	ret = write( fd, data->data, data->len );
 	free( data->data );
 	free( data->data );
-	if (ret != data->len || (ret = fsync( fd ))) {
+	if (ret != data->len || ((FSyncLevel >= FSYNC_NORMAL) && (ret = fsync( fd )))) {
 		if (ret < 0)
 		if (ret < 0)
 			sys_error( "Maildir error: cannot write %s", buf );
 			sys_error( "Maildir error: cannot write %s", buf );
 		else
 		else

+ 6 - 0
src/isync.h

@@ -481,6 +481,12 @@ extern group_conf_t *groups;
 extern int global_ops[2];
 extern int global_ops[2];
 extern char *global_sync_state;
 extern char *global_sync_state;
 
 
+#define FSYNC_NONE     0
+#define FSYNC_NORMAL   1
+#define FSYNC_THOROUGH 2
+
+extern int FSyncLevel;
+
 int parse_bool( conffile_t *cfile );
 int parse_bool( conffile_t *cfile );
 int parse_int( conffile_t *cfile );
 int parse_int( conffile_t *cfile );
 int parse_size( conffile_t *cfile );
 int parse_size( conffile_t *cfile );

+ 21 - 1
src/mbsync.1

@@ -20,7 +20,7 @@
 \" As a special exception, mbsync may be linked with the OpenSSL library,
 \" As a special exception, mbsync may be linked with the OpenSSL library,
 \" despite that library's more restrictive license.
 \" despite that library's more restrictive license.
 ..
 ..
-.TH mbsync 1 "2012 Aug 25"
+.TH mbsync 1 "2012 Sep 15"
 ..
 ..
 .SH NAME
 .SH NAME
 mbsync - synchronize IMAP4 and Maildir mailboxes
 mbsync - synchronize IMAP4 and Maildir mailboxes
@@ -477,6 +477,26 @@ line, except that there newlines can be used as mailbox name separators as well.
 Add the specified channels to the group. This option can be specified multiple
 Add the specified channels to the group. This option can be specified multiple
 times within a Group.
 times within a Group.
 ..
 ..
+.SS Global Options
+.TP
+\fBFSync\fR {\fINone\fR|\fINormal\fR|\fIThorough\fR}
+.br
+Select the amount of forced flushing \fBmbsync\fR performs, which determines
+the level of data safety after system crashes and power outages:
+.br
+\fBNone\fR - no flushing at all. This is reasonably safe for file systems
+which are mounted with data=ordered mode.
+.br
+\fBNormal\fR - message and critical metadata writes are flushed. No data
+should be lost due to crashes, though it is still possible that messages
+are duplicated after crashes. This is the default, and is a wise choice for
+file systems mounted with data=writeback, in particular modern systems like
+ext4, btrfs and xfs. The performance impact on older file systems may be
+disproportionate.
+.br
+\fBThorough\fR - this avoids message duplication after crashes as well,
+at some additional performance cost.
+..
 .SH SSL CERTIFICATES
 .SH SSL CERTIFICATES
 [to be done]
 [to be done]
 ..
 ..

+ 3 - 1
src/run-tests.pl

@@ -256,7 +256,9 @@ sub writecfg($$$)
 	open(FILE, ">", ".mbsyncrc") or
 	open(FILE, ">", ".mbsyncrc") or
 		die "Cannot open .mbsyncrc.\n";
 		die "Cannot open .mbsyncrc.\n";
 	print FILE
 	print FILE
-"MaildirStore master
+"FSync None
+
+MaildirStore master
 Path ./
 Path ./
 Inbox ./master
 Inbox ./master
 ".shift()."
 ".shift()."

+ 3 - 2
src/sync.c

@@ -42,7 +42,7 @@ const char *str_ms[] = { "master", "slave" }, *str_hl[] = { "push", "pull" };
 void
 void
 Fclose( FILE *f, int safe )
 Fclose( FILE *f, int safe )
 {
 {
-	if ((safe && (fflush( f ) || fdatasync( fileno( f ) ))) || fclose( f ) == EOF) {
+	if ((safe && (fflush( f ) || (FSyncLevel >= FSYNC_NORMAL && fdatasync( fileno( f ) )))) || fclose( f ) == EOF) {
 		sys_error( "Error: cannot close file. Disk full?" );
 		sys_error( "Error: cannot close file. Disk full?" );
 		exit( 1 );
 		exit( 1 );
 	}
 	}
@@ -1202,7 +1202,8 @@ box_loaded( int sts, void *aux )
 						cv->srec = srec;
 						cv->srec = srec;
 						cv->msg = tmsg;
 						cv->msg = tmsg;
 						Fprintf( svars->jfp, "# %d %d %." stringify(TUIDL) "s\n", srec->uid[M], srec->uid[S], srec->tuid );
 						Fprintf( svars->jfp, "# %d %d %." stringify(TUIDL) "s\n", srec->uid[M], srec->uid[S], srec->tuid );
-						fdatasync( fileno( svars->jfp ) );
+						if (FSyncLevel >= FSYNC_THOROUGH)
+							fdatasync( fileno( svars->jfp ) );
 						debug( "  -> %sing message, TUID %." stringify(TUIDL) "s\n", str_hl[t], srec->tuid );
 						debug( "  -> %sing message, TUID %." stringify(TUIDL) "s\n", str_hl[t], srec->tuid );
 						if (copy_msg( cv ))
 						if (copy_msg( cv ))
 							return;
 							return;