changeset 21454:447f2259ec6c

lib-storage: Lock autoexpunging so only a single process does it. This hopefully helps to avoid duplicates with lazy_expunge plugin.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Thu, 26 Jan 2017 22:42:09 +0200
parents 8d25d3692e26
children a927a4f7aab1
files src/lib-storage/mail-autoexpunge.c
diffstat 1 files changed, 54 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/mail-autoexpunge.c	Sun Jan 29 01:08:23 2017 +0200
+++ b/src/lib-storage/mail-autoexpunge.c	Thu Jan 26 22:42:09 2017 +0200
@@ -2,12 +2,54 @@
 
 #include "lib.h"
 #include "ioloop.h"
+#include "file-create-locked.h"
 #include "mailbox-list-iter.h"
 #include "mail-storage-private.h"
 #include "mail-namespace.h"
 #include "mail-user.h"
 #include "mail-autoexpunge.h"
 
+#define AUTOEXPUNGE_LOCK_FNAME "dovecot.autoexpunge.lock"
+
+struct mailbox_autoexpunge_lock {
+	const char *path;
+	struct file_lock *lock;
+	int fd;
+};
+
+static void mailbox_autoexpunge_lock(struct mail_user *user,
+				     struct mailbox_autoexpunge_lock *lock)
+{
+	struct file_create_settings lock_set;
+	bool created;
+	const char *home, *error;
+	int ret;
+
+	/* Try to lock the autoexpunging. If the lock already exists, another
+	   process is already busy with expunging, so we don't have to do it.
+	   The easiest place where to store the lock file to is the home
+	   directory, but allow autoexpunging to work even if we can't get
+	   it. The lock isn't really required; it 1) improves performance
+	   so that multiple processes won't do the same work unnecessarily,
+	   and 2) it helps to avoid duplicates mails being added with
+	   lazy_expunge. */
+	if ((ret = mail_user_get_home(user, &home)) > 0) {
+		const struct mail_storage_settings *mail_set =
+			mail_user_set_get_storage_set(user);
+		i_zero(&lock_set);
+		lock_set.lock_method = mail_set->parsed_lock_method,
+		lock->path = t_strdup_printf("%s/"AUTOEXPUNGE_LOCK_FNAME, home);
+		lock->fd = file_create_locked(lock->path, &lock_set,
+					      &lock->lock, &created, &error);
+		if (lock->fd == -1) {
+			if (errno != EAGAIN && errno != ENOENT)
+				i_error("autoexpunge: Couldn't lock %s: %s", lock->path, error);
+		}
+	} else if (ret == 0) {
+		i_warning("autoexpunge: User has no home directory, can't lock");
+	}
+}
+
 static int
 mailbox_autoexpunge(struct mailbox *box, unsigned int interval_time,
 		    unsigned int max_mails)
@@ -132,7 +174,9 @@
 	}
 }
 
-static void mail_namespace_autoexpunge(struct mail_namespace *ns)
+static void
+mail_namespace_autoexpunge(struct mail_namespace *ns,
+			   struct mailbox_autoexpunge_lock *lock)
 {
 	struct mailbox_settings *const *box_set;
 	const char *vname;
@@ -145,6 +189,8 @@
 		    (*box_set)->autoexpunge_max_mails == 0)
 			continue;
 
+		mailbox_autoexpunge_lock(ns->user, lock);
+
 		if (strpbrk((*box_set)->name, "*?") != NULL)
 			mailbox_autoexpunge_wildcards(ns, *box_set);
 		else {
@@ -161,10 +207,16 @@
 
 void mail_user_autoexpunge(struct mail_user *user)
 {
+	struct mailbox_autoexpunge_lock lock = { .fd = -1 };
 	struct mail_namespace *ns;
 
 	for (ns = user->namespaces; ns != NULL; ns = ns->next) {
 		if (ns->alias_for == NULL)
-			mail_namespace_autoexpunge(ns);
+			mail_namespace_autoexpunge(ns, &lock);
+	}
+	if (lock.fd != -1) {
+		i_unlink(lock.path);
+		i_close_fd(&lock.fd);
+		file_lock_free(&lock.lock);
 	}
 }