git » libjio » commit b1944ad

This patch implement lingering transactions.

author Alberto Bertogli
2004-07-13 21:16:43 UTC
committer Alberto Bertogli
2007-07-15 13:10:37 UTC
parent 984a6da0404a35f58b3e75e4e6787baf6c52535b

This patch implement lingering transactions.

This patch implement lingering transactions.

It allows you to trade performance with recovery time, by using a different
approach to journaling.

Normally, you would open the file O_SYNC, and when commiting a transaction
data gets written twice: once in the transaction file, and once in the real
file. Then, after the last write, the transaction file is removed.

What this patch do is allow you to avoid opening the file O_SYNC, and thus
writing the data synchronously only once, speeding up operations. The OS will
do the writeouts asynchronous when it think the time is right.

In this mode, the transaction file is written synchronously, but instead of
then writing to the real file with O_SYNC and then removing the transaction
file, we do the write _without_ O_SYNC and leave the transaction file.

In case of a crash, the transaction files will still be there for recovery, so
there's no risk of losing data.

libjio.h +10 -0
trans.c +41 -5

diff --git a/libjio.h b/libjio.h
index 968038c..777b792 100644
--- a/libjio.h
+++ b/libjio.h
@@ -27,6 +27,7 @@ struct jfs {
 	int jdirfd;		/* journal directory file descriptor */
 	int jfd;		/* journal's lock file descriptor */
 	int flags;		/* journal flags */
+	struct jlinger *ltrans;	/* lingered transactions */
 	pthread_mutex_t lock;	/* a soft lock used in some operations */
 };
 
@@ -53,6 +54,13 @@ struct jtrans {
 	struct joper *op;	/* list of operations */
 };
 
+/* lingered transaction */
+struct jlinger {
+	int id;			/* transaction id */
+	char *name;		/* name of the transaction file */
+	struct jlinger *next;
+};
+
 struct jfsck_result {
 	int total;		/* total transactions files we looked at */
 	int invalid;		/* invalid files in the journal directory */
@@ -88,6 +96,7 @@ int jtrans_add(struct jtrans *ts, const void *buf, size_t count, off_t offset);
 int jtrans_commit(struct jtrans *ts);
 int jtrans_rollback(struct jtrans *ts);
 void jtrans_free(struct jtrans *ts);
+void jsync(struct jfs *fs);
 int jclose(struct jfs *fs);
 
 
@@ -123,6 +132,7 @@ FILE *jfsopen(struct jfs *stream, const char *mode);
 /* jfs constants */
 #define J_NOLOCK	1	/* don't lock the file before operating on it */
 #define J_NOROLLBACK	2	/* no need to read rollback information */
+#define J_LINGER	3	/* use lingering transactions */
 
 /* jtrans constants */
 #define J_COMMITED	1	/* mark a transaction as commited */
diff --git a/trans.c b/trans.c
index af392f4..8c08e03 100644
--- a/trans.c
+++ b/trans.c
@@ -45,7 +45,7 @@ static unsigned int get_tid(struct jfs *fs)
 	/* increment it and handle overflows */
 	rv = curid + 1;
 	if (rv == 0)
-		rv = 1;
+		goto exit;
 
 	/* write to the file descriptor */
 	r = spwrite(fs->jfd, &rv, sizeof(rv), 0);
@@ -197,6 +197,7 @@ int jtrans_commit(struct jtrans *ts)
 	char *name;
 	unsigned char *buf_init, *bufp;
 	struct joper *op;
+	struct jlinger *linger;
 	off_t curpos = 0;
 	size_t written = 0;
 
@@ -355,10 +356,22 @@ int jtrans_commit(struct jtrans *ts)
 		written += rv;
 	}
 
-	/* the transaction has been applied, so we cleanup and remove it from
-	 * the disk */
-	free_tid(ts->fs, ts->id);
-	unlink(name);
+	if (ts->flags & J_LINGER) {
+		linger = malloc(sizeof(struct jlinger));
+		if (linger == NULL)
+			goto exit;
+
+		linger->id = id;
+		linger->name = strdup(name);
+		linger->next = ts->fs->ltrans;
+
+		ts->fs->ltrans = linger;
+	} else {
+		/* the transaction has been applied, so we cleanup and remove
+		 * it from the disk */
+		free_tid(ts->fs, ts->id);
+		unlink(name);
+	}
 
 	/* mark the transaction as commited, _after_ it was removed */
 	ts->flags = ts->flags | J_COMMITED;
@@ -466,6 +479,7 @@ int jopen(struct jfs *fs, const char *name, int flags, int mode, int jflags)
 	fs->fd = fd;
 	fs->name = strdup(name);
 	fs->flags = jflags;
+	fs->ltrans = NULL;
 
 	/* Note on fs->lock usage: this lock is used only inside the wrappers,
 	 * and exclusively to protect the file pointer. This means that it
@@ -519,9 +533,31 @@ int jopen(struct jfs *fs, const char *name, int flags, int mode, int jflags)
 	return fd;
 }
 
+/* sync a file (makes sense only if using lingering transactions) */
+void jsync(struct jfs *fs)
+{
+	struct jlinger *linger, *ltmp;
+
+	fsync(fs->fd);
+
+	linger = fs->ltrans;
+	while (linger != NULL) {
+		free_tid(fs, linger->id);
+		unlink(linger->name);
+		free(linger->name);
+
+		ltmp = linger->next;
+		free(linger);
+
+		linger = ltmp;
+	}
+}
+
 /* close a file */
 int jclose(struct jfs *fs)
 {
+	jsync(fs);
+
 	if (close(fs->fd))
 		return -1;
 	if (close(fs->jfd))