dovecot-1.2: dict: Support large iterations by sending data in o...

dovecot at dovecot.org dovecot at dovecot.org
Wed Aug 27 08:28:43 EEST 2008


details:   http://hg.dovecot.org/dovecot-1.2/rev/1cf9844b7a20
changeset: 8110:1cf9844b7a20
user:      Timo Sirainen <tss at iki.fi>
date:      Wed Aug 27 08:27:35 2008 +0300
description:
dict: Support large iterations by sending data in output stream flush callback.
Fixes also a hang if a lot of changes were done during the iteration.

diffstat:

1 file changed, 59 insertions(+), 20 deletions(-)
src/dict/dict-server.c |   79 +++++++++++++++++++++++++++++++++++-------------

diffs (139 lines):

diff -r e7929190cd32 -r 1cf9844b7a20 src/dict/dict-server.c
--- a/src/dict/dict-server.c	Wed Aug 27 08:21:39 2008 +0300
+++ b/src/dict/dict-server.c	Wed Aug 27 08:27:35 2008 +0300
@@ -6,12 +6,15 @@
 #include "network.h"
 #include "istream.h"
 #include "ostream.h"
+#include "str.h"
 #include "dict.h"
 #include "dict-client.h"
 #include "dict-server.h"
 
 #include <stdlib.h>
 #include <unistd.h>
+
+#define DICT_OUTPUT_OPTIMAL_SIZE 1024
 
 struct dict_server_transaction {
 	unsigned int id;
@@ -32,6 +35,8 @@ struct dict_client_connection {
 	struct istream *input;
 	struct ostream *output;
 
+	struct dict_iterate_context *iter_ctx;
+
 	/* There are only a few transactions per client, so keeping them in
 	   array is fast enough */
 	ARRAY_DEFINE(transactions, struct dict_server_transaction);
@@ -57,6 +62,11 @@ static int cmd_lookup(struct dict_client
 	const char *reply;
 	const char *value;
 	int ret;
+
+	if (conn->iter_ctx != NULL) {
+		i_error("dict client: LOOKUP: Can't lookup while iterating");
+		return -1;
+	}
 
 	/* <key> */
 	ret = dict_lookup(conn->dict, pool_datastack_create(), line, &value);
@@ -73,12 +83,45 @@ static int cmd_lookup(struct dict_client
 	return 0;
 }
 
-static int cmd_iterate(struct dict_client_connection *conn, const char *line)
-{
-	struct dict_iterate_context *ctx;
-	const char *const *args;
+static int cmd_iterate_flush(struct dict_client_connection *conn)
+{
+	string_t *str;
 	const char *key, *value;
 	int ret;
+
+	str = t_str_new(256);
+	o_stream_cork(conn->output);
+	while ((ret = dict_iterate(conn->iter_ctx, &key, &value)) > 0) {
+		str_truncate(str, 0);
+		str_printfa(str, "%s\t%s\n", key, value);
+		o_stream_send(conn->output, str_data(str), str_len(str));
+
+		if (o_stream_get_buffer_used_size(conn->output) >
+		    DICT_OUTPUT_OPTIMAL_SIZE) {
+			if (o_stream_flush(conn->output) <= 0)
+				break;
+			/* flushed everything, continue */
+		}
+	}
+
+	if (ret <= 0) {
+		/* finished iterating */
+		o_stream_unset_flush_callback(conn->output);
+		dict_iterate_deinit(&conn->iter_ctx);
+		o_stream_send(conn->output, "\n", 1);
+	}
+	o_stream_uncork(conn->output);
+	return ret <= 0 ? 1 : 0;
+}
+
+static int cmd_iterate(struct dict_client_connection *conn, const char *line)
+{
+	const char *const *args;
+
+	if (conn->iter_ctx != NULL) {
+		i_error("dict client: ITERATE: Already iterating");
+		return -1;
+	}
 
 	args = t_strsplit(line, "\t");
 	if (str_array_length(args) != 2) {
@@ -87,22 +130,10 @@ static int cmd_iterate(struct dict_clien
 	}
 
 	/* <flags> <path> */
-	o_stream_cork(conn->output);
-	ctx = dict_iterate_init(conn->dict, args[1], atoi(args[0]));
-	while ((ret = dict_iterate(ctx, &key, &value)) > 0) {
-		/* FIXME: we don't want to keep blocking here. set a flush
-		   function and send the replies there when buffer gets full */
-		T_BEGIN {
-			const char *reply;
-
-			reply = t_strdup_printf("%s\t%s\n", key, value);
-			o_stream_send_str(conn->output, reply);
-		} T_END;
-	}
-	dict_iterate_deinit(&ctx);
-
-	o_stream_send_str(conn->output, "\n");
-	o_stream_uncork(conn->output);
+	conn->iter_ctx = dict_iterate_init(conn->dict, args[1], atoi(args[0]));
+
+	o_stream_set_flush_callback(conn->output, cmd_iterate_flush, conn);
+	cmd_iterate_flush(conn);
 	return 0;
 }
 
@@ -192,6 +223,11 @@ static int cmd_commit(struct dict_client
 	struct dict_server_transaction *trans;
 	const char *reply;
 	int ret;
+
+	if (conn->iter_ctx != NULL) {
+		i_error("dict client: COMMIT: Can't commit while iterating");
+		return -1;
+	}
 
 	if (dict_server_transaction_lookup_parse(conn, line, &trans) < 0)
 		return -1;
@@ -431,6 +467,9 @@ static void dict_client_connection_deini
 		array_free(&conn->transactions);
 	}
 
+	if (conn->iter_ctx != NULL)
+		dict_iterate_deinit(&conn->iter_ctx);
+
 	io_remove(&conn->io);
 	i_stream_destroy(&conn->input);
 	o_stream_destroy(&conn->output);


More information about the dovecot-cvs mailing list