#include "dict-commands.h"
#include "main.h"
+#define DICT_CLIENT_PROTOCOL_TIMINGS_MIN_VERSION 1
#define DICT_OUTPUT_OPTIMAL_SIZE 1024
struct dict_cmd_func {
if (!dict_settings->verbose_proctitle)
return;
- io_loop_time_refresh();
diff = timeval_diff_usecs(&ioloop_timeval, &cmd->start_timeval);
if (diff < 0)
diff = 0;
dict_proctitle_update_later();
}
+static void
+dict_cmd_reply_handle_timings(struct dict_connection_cmd *cmd,
+ string_t *str, struct timing *timing)
+{
+ io_loop_time_refresh();
+ cmd_stats_update(cmd, timing);
+
+ if (cmd->conn->minor_version < DICT_CLIENT_PROTOCOL_TIMINGS_MIN_VERSION)
+ return;
+ str_printfa(str, "\t%ld\t%u\t%ld\t%u",
+ (long)cmd->start_timeval.tv_sec,
+ (unsigned int)cmd->start_timeval.tv_usec,
+ (long)ioloop_timeval.tv_sec,
+ (unsigned int)ioloop_timeval.tv_usec);
+}
+
static void
cmd_lookup_callback(const struct dict_lookup_result *result, void *context)
{
struct dict_connection_cmd *cmd = context;
-
- cmd_stats_update(cmd, cmd_stats.lookups);
+ string_t *str = t_str_new(128);
if (result->ret > 0) {
- cmd->reply = i_strdup_printf("%c%s\n",
- DICT_PROTOCOL_REPLY_OK, str_tabescape(result->value));
+ str_append_c(str, DICT_PROTOCOL_REPLY_OK);
+ str_append_tabescaped(str, result->value);
} else if (result->ret == 0) {
- cmd->reply = i_strdup_printf("%c\n", DICT_PROTOCOL_REPLY_NOTFOUND);
+ str_append_c(str, DICT_PROTOCOL_REPLY_NOTFOUND);
} else {
i_error("%s", result->error);
- cmd->reply = i_strdup_printf("%c\n", DICT_PROTOCOL_REPLY_FAIL);
+ str_append_c(str, DICT_PROTOCOL_REPLY_FAIL);
+ str_append_tabescaped(str, result->error);
}
+ dict_cmd_reply_handle_timings(cmd, str, cmd_stats.lookups);
+ str_append_c(str, '\n');
+
+ cmd->reply = i_strdup(str_c(str));
dict_connection_cmds_flush(cmd->conn);
}
str_truncate(str, 0);
if (dict_iterate_deinit(&cmd->iter) < 0)
str_append_c(str, DICT_PROTOCOL_REPLY_FAIL);
+ dict_cmd_reply_handle_timings(cmd, str, cmd_stats.iterations);
str_append_c(str, '\n');
o_stream_uncork(cmd->conn->output);
- cmd_stats_update(cmd, cmd_stats.iterations);
cmd->reply = i_strdup(str_c(str));
return 1;
}
static void
cmd_commit_finish(struct dict_connection_cmd *cmd, int ret, bool async)
{
+ string_t *str = t_str_new(64);
char chr;
- cmd_stats_update(cmd, cmd_stats.commits);
-
switch (ret) {
case 1:
chr = DICT_PROTOCOL_REPLY_OK;
break;
}
if (async) {
- cmd->reply = i_strdup_printf("%c%c%u\n",
+ str_printfa(str, "%c%c%u",
DICT_PROTOCOL_REPLY_ASYNC_COMMIT, chr, cmd->trans_id);
} else {
- cmd->reply = i_strdup_printf("%c%u\n", chr, cmd->trans_id);
+ str_printfa(str, "%c%u", chr, cmd->trans_id);
}
+ dict_cmd_reply_handle_timings(cmd, str, cmd_stats.commits);
+ str_append_c(str, '\n');
+ cmd->reply = i_strdup(str_c(str));
+
dict_connection_transaction_array_remove(cmd->conn, cmd->trans_id);
dict_connection_cmds_flush(cmd->conn);
}
lock_msecs/1000, lock_msecs%1000);
}
-static const char *dict_warnings_sec(const struct client_dict_cmd *cmd, int msecs)
+static const char *
+dict_warnings_sec(const struct client_dict_cmd *cmd, int msecs,
+ const char *const *extra_args)
{
- return t_strdup_printf("%d.%03d secs (%s)", msecs/1000, msecs%1000,
- dict_wait_warnings(cmd));
+ string_t *str = t_str_new(64);
+ struct timeval tv_start, tv_end;
+ unsigned int tv_start_usec, tv_end_usec;
+
+ str_printfa(str, "%d.%03d secs (%s", msecs/1000, msecs%1000,
+ dict_wait_warnings(cmd));
+ if (str_array_length(extra_args) >= 4 &&
+ str_to_time(extra_args[0], &tv_start.tv_sec) == 0 &&
+ str_to_uint(extra_args[1], &tv_start_usec) == 0 &&
+ str_to_time(extra_args[2], &tv_end.tv_sec) == 0 &&
+ str_to_uint(extra_args[3], &tv_end_usec) == 0) {
+ tv_start.tv_usec = tv_start_usec;
+ tv_end.tv_usec = tv_end_usec;
+
+ int server_msecs_since_start =
+ timeval_diff_msecs(&ioloop_timeval, &tv_start);
+ int server_msecs = timeval_diff_msecs(&tv_end, &tv_start);
+ str_printfa(str, ", started on dict-server %u.%03d secs ago, "
+ "took %u.%03d secs",
+ server_msecs_since_start/1000,
+ server_msecs_since_start%1000,
+ server_msecs/1000, server_msecs%1000);
+ }
+ str_append_c(str, ')');
+ return str_c(str);
}
static void
client_dict_lookup_async_callback(struct client_dict_cmd *cmd,
enum dict_protocol_reply reply,
const char *value,
- const char *const *extra_args ATTR_UNUSED,
+ const char *const *extra_args,
const char *error,
bool disconnected ATTR_UNUSED)
{
if (result.error != NULL) {
/* include timing info always in error messages */
result.error = t_strdup_printf("%s (reply took %s)",
- result.error, dict_warnings_sec(cmd, diff));
+ result.error, dict_warnings_sec(cmd, diff, extra_args));
} else if (!cmd->background &&
diff >= DICT_CLIENT_REQUEST_WARN_TIMEOUT_MSECS) {
i_warning("read(%s): dict lookup took %s: %s",
- dict->conn.conn.name, dict_warnings_sec(cmd, diff),
+ dict->conn.conn.name, dict_warnings_sec(cmd, diff, extra_args),
cmd->query);
}
static void
client_dict_iter_api_callback(struct client_dict_iterate_context *ctx,
- struct client_dict_cmd *cmd)
+ struct client_dict_cmd *cmd,
+ const char *const *extra_args)
{
struct client_dict *dict = cmd->dict;
if (ctx->error != NULL) {
/* include timing info always in error messages */
char *new_error = i_strdup_printf("%s (reply took %s)",
- ctx->error, dict_warnings_sec(cmd, diff));
+ ctx->error, dict_warnings_sec(cmd, diff, extra_args));
i_free(ctx->error);
ctx->error = new_error;
} else if (!cmd->background &&
diff >= DICT_CLIENT_REQUEST_WARN_TIMEOUT_MSECS) {
i_warning("read(%s): dict iteration took %s: %s",
- dict->conn.conn.name, dict_warnings_sec(cmd, diff),
+ dict->conn.conn.name, dict_warnings_sec(cmd, diff, extra_args),
cmd->query);
}
}
case DICT_PROTOCOL_REPLY_ITER_FINISHED:
/* end of iteration */
ctx->finished = TRUE;
- client_dict_iter_api_callback(ctx, cmd);
+ client_dict_iter_api_callback(ctx, cmd, extra_args);
client_dict_iterate_free(ctx);
return;
case DICT_PROTOCOL_REPLY_OK:
/* key \t value */
iter_key = value;
iter_value = extra_args[0];
+ extra_args++;
break;
case DICT_PROTOCOL_REPLY_FAIL:
error = t_strdup_printf("dict-server returned failure: %s", value);
if (ctx->error == NULL)
ctx->error = i_strdup(error);
ctx->finished = TRUE;
- client_dict_iter_api_callback(ctx, cmd);
+ client_dict_iter_api_callback(ctx, cmd, extra_args);
client_dict_iterate_free(ctx);
return;
}
result->key = p_strdup(ctx->results_pool, iter_key);
result->value = p_strdup(ctx->results_pool, iter_value);
- client_dict_iter_api_callback(ctx, cmd);
+ client_dict_iter_api_callback(ctx, cmd, NULL);
}
static struct dict_iterate_context *
ret = DICT_COMMIT_RET_WRITE_UNCERTAIN;
/* fallthrough */
case DICT_PROTOCOL_REPLY_FAIL: {
+ /* value contains the obsolete trans_id */
const char *error = extra_args[0];
i_error("dict-client: server returned failure: %s "
"(reply took %u.%03u secs)",
error != NULL ? error : "",
diff/1000, diff%1000);
+ if (error != NULL)
+ extra_args++;
break;
}
default:
diff >= DICT_CLIENT_REQUEST_WARN_TIMEOUT_MSECS) {
i_warning("read(%s): dict commit took %s: "
"%s (%u commands, first: %s)",
- dict->conn.conn.name, dict_warnings_sec(cmd, diff),
+ dict->conn.conn.name, dict_warnings_sec(cmd, diff, extra_args),
cmd->query, cmd->trans->query_count,
cmd->trans->first_query);
}