comparison src/lib-storage/index/imapc/imapc-sync.c @ 22041:21fab4826117

imapc: Try to merge STOREs together as much as possible when syncing
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Thu, 04 May 2017 19:59:41 +0300
parents 3acdf933fc65
children c24c32983eae
comparison
equal deleted inserted replaced
22040:99752d3e656b 22041:21fab4826117
1 /* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */ 1 /* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */
2 2
3 #include "lib.h" 3 #include "lib.h"
4 #include "ioloop.h" 4 #include "ioloop.h"
5 #include "hash.h"
5 #include "str.h" 6 #include "str.h"
7 #include "sort.h"
6 #include "imap-util.h" 8 #include "imap-util.h"
7 #include "mail-cache.h" 9 #include "mail-cache.h"
8 #include "mail-index-modseq.h" 10 #include "mail-index-modseq.h"
9 #include "index-sync-private.h" 11 #include "index-sync-private.h"
10 #include "imapc-msgmap.h" 12 #include "imapc-msgmap.h"
73 imapc_sync_cmd(struct imapc_sync_context *ctx, const char *cmd_str) 75 imapc_sync_cmd(struct imapc_sync_context *ctx, const char *cmd_str)
74 { 76 {
75 return imapc_sync_cmd_full(ctx, cmd_str, FALSE); 77 return imapc_sync_cmd_full(ctx, cmd_str, FALSE);
76 } 78 }
77 79
78 static struct imapc_command * 80 static unsigned int imapc_sync_store_hash(const struct imapc_sync_store *store)
79 imapc_sync_store_cmd(struct imapc_sync_context *ctx, const char *cmd_str) 81 {
80 { 82 return str_hash(store->flags) ^ store->modify_type;
81 return imapc_sync_cmd_full(ctx, cmd_str, TRUE); 83 }
84
85 static int imapc_sync_store_cmp(const struct imapc_sync_store *store1,
86 const struct imapc_sync_store *store2)
87 {
88 if (store1->modify_type != store2->modify_type)
89 return 1;
90 return strcmp(store1->flags, store2->flags);
91 }
92
93 static const char *imapc_sync_flags_sort(const char *flags)
94 {
95 if (strchr(flags, ' ') == NULL)
96 return flags;
97
98 const char **str = t_strsplit(flags, " ");
99 i_qsort(str, str_array_length(str), sizeof(const char *),
100 i_strcasecmp_p);
101 return t_strarray_join(str, " ");
102 }
103
104 static void
105 imapc_sync_store_flush(struct imapc_sync_context *ctx)
106 {
107 struct imapc_sync_store *store;
108 const char *sorted_flags;
109
110 if (ctx->prev_uid1 == 0)
111 return;
112
113 sorted_flags = imapc_sync_flags_sort(str_c(ctx->prev_flags));
114 struct imapc_sync_store store_lookup = {
115 .modify_type = ctx->prev_modify_type,
116 .flags = sorted_flags,
117 };
118 store = hash_table_lookup(ctx->stores, &store_lookup);
119 if (store == NULL) {
120 store = p_new(ctx->pool, struct imapc_sync_store, 1);
121 store->modify_type = ctx->prev_modify_type;
122 store->flags = p_strdup(ctx->pool, sorted_flags);
123 p_array_init(&store->uids, ctx->pool, 4);
124 hash_table_insert(ctx->stores, store, store);
125 }
126 seq_range_array_add_range(&store->uids, ctx->prev_uid1, ctx->prev_uid2);
127 }
128
129 static void
130 imapc_sync_store(struct imapc_sync_context *ctx,
131 enum modify_type modify_type, uint32_t uid1, uint32_t uid2,
132 const char *flags)
133 {
134 if (ctx->prev_flags == NULL) {
135 ctx->prev_flags = str_new(ctx->pool, 128);
136 hash_table_create(&ctx->stores, ctx->pool, 0,
137 imapc_sync_store_hash, imapc_sync_store_cmp);
138 }
139
140 if (ctx->prev_uid1 != uid1 || ctx->prev_uid2 != uid2 ||
141 ctx->prev_modify_type != modify_type) {
142 imapc_sync_store_flush(ctx);
143 ctx->prev_uid1 = uid1;
144 ctx->prev_uid2 = uid2;
145 ctx->prev_modify_type = modify_type;
146 str_truncate(ctx->prev_flags, 0);
147 }
148 if (str_len(ctx->prev_flags) > 0)
149 str_append_c(ctx->prev_flags, ' ');
150 str_append(ctx->prev_flags, flags);
151 }
152
153 static void
154 imapc_sync_finish_store(struct imapc_sync_context *ctx)
155 {
156 struct hash_iterate_context *iter;
157 struct imapc_sync_store *store;
158 string_t *cmd = t_str_new(128);
159
160 imapc_sync_store_flush(ctx);
161
162 if (!hash_table_is_created(ctx->stores))
163 return;
164
165 iter = hash_table_iterate_init(ctx->stores);
166 while (hash_table_iterate(iter, ctx->stores, &store, &store)) {
167 str_truncate(cmd, 0);
168 str_append(cmd, "UID STORE ");
169 imap_write_seq_range(cmd, &store->uids);
170 str_printfa(cmd, " %cFLAGS (%s)",
171 store->modify_type == MODIFY_ADD ? '+' : '-',
172 store->flags);
173 imapc_sync_cmd_full(ctx, str_c(cmd), TRUE);
174 }
175 hash_table_iterate_deinit(&iter);
176 hash_table_destroy(&ctx->stores);
82 } 177 }
83 178
84 static void 179 static void
85 imapc_sync_add_missing_deleted_flags(struct imapc_sync_context *ctx, 180 imapc_sync_add_missing_deleted_flags(struct imapc_sync_context *ctx,
86 uint32_t seq1, uint32_t seq2) 181 uint32_t seq1, uint32_t seq2)
87 { 182 {
88 const struct mail_index_record *rec; 183 const struct mail_index_record *rec;
89 uint32_t seq, uid1, uid2; 184 uint32_t seq, uid1, uid2;
90 const char *cmd;
91 185
92 /* if any of them has a missing \Deleted flag, 186 /* if any of them has a missing \Deleted flag,
93 just add it to all of them. */ 187 just add it to all of them. */
94 for (seq = seq1; seq <= seq2; seq++) { 188 for (seq = seq1; seq <= seq2; seq++) {
95 rec = mail_index_lookup(ctx->sync_view, seq); 189 rec = mail_index_lookup(ctx->sync_view, seq);
98 } 192 }
99 193
100 if (seq <= seq2) { 194 if (seq <= seq2) {
101 mail_index_lookup_uid(ctx->sync_view, seq1, &uid1); 195 mail_index_lookup_uid(ctx->sync_view, seq1, &uid1);
102 mail_index_lookup_uid(ctx->sync_view, seq2, &uid2); 196 mail_index_lookup_uid(ctx->sync_view, seq2, &uid2);
103 cmd = t_strdup_printf("UID STORE %u:%u +FLAGS \\Deleted", 197
104 uid1, uid2); 198 imapc_sync_store(ctx, MODIFY_ADD, uid1, uid2, "\\Deleted");
105 imapc_sync_store_cmd(ctx, cmd);
106 } 199 }
107 } 200 }
108 201
109 static void imapc_sync_index_flags(struct imapc_sync_context *ctx, 202 static void imapc_sync_index_flags(struct imapc_sync_context *ctx,
110 const struct mail_index_sync_rec *sync_rec) 203 const struct mail_index_sync_rec *sync_rec)
113 206
114 i_assert(sync_rec->type == MAIL_INDEX_SYNC_TYPE_FLAGS); 207 i_assert(sync_rec->type == MAIL_INDEX_SYNC_TYPE_FLAGS);
115 208
116 if (sync_rec->add_flags != 0) { 209 if (sync_rec->add_flags != 0) {
117 i_assert((sync_rec->add_flags & MAIL_RECENT) == 0); 210 i_assert((sync_rec->add_flags & MAIL_RECENT) == 0);
118 str_printfa(str, "UID STORE %u:%u +FLAGS (", 211
119 sync_rec->uid1, sync_rec->uid2);
120 imap_write_flags(str, sync_rec->add_flags, NULL); 212 imap_write_flags(str, sync_rec->add_flags, NULL);
121 str_append_c(str, ')'); 213 imapc_sync_store(ctx, MODIFY_ADD, sync_rec->uid1,
122 imapc_sync_store_cmd(ctx, str_c(str)); 214 sync_rec->uid2, str_c(str));
123 } 215 }
124 216
125 if (sync_rec->remove_flags != 0) { 217 if (sync_rec->remove_flags != 0) {
126 i_assert((sync_rec->remove_flags & MAIL_RECENT) == 0); 218 i_assert((sync_rec->remove_flags & MAIL_RECENT) == 0);
127 str_truncate(str, 0); 219 str_truncate(str, 0);
128 str_printfa(str, "UID STORE %u:%u -FLAGS (",
129 sync_rec->uid1, sync_rec->uid2);
130 imap_write_flags(str, sync_rec->remove_flags, NULL); 220 imap_write_flags(str, sync_rec->remove_flags, NULL);
131 str_append_c(str, ')'); 221 imapc_sync_store(ctx, MODIFY_REMOVE, sync_rec->uid1,
132 imapc_sync_store_cmd(ctx, str_c(str)); 222 sync_rec->uid2, str_c(str));
133 } 223 }
134 } 224 }
135 225
136 static void 226 static void
137 imapc_sync_index_keyword(struct imapc_sync_context *ctx, 227 imapc_sync_index_keyword(struct imapc_sync_context *ctx,
138 const struct mail_index_sync_rec *sync_rec) 228 const struct mail_index_sync_rec *sync_rec)
139 { 229 {
140 string_t *str = t_str_new(128);
141 const char *const *kw_p; 230 const char *const *kw_p;
142 char change_char; 231 enum modify_type modify_type;
143 232
144 switch (sync_rec->type) { 233 switch (sync_rec->type) {
145 case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD: 234 case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD:
146 change_char = '+'; 235 modify_type = MODIFY_ADD;
147 break; 236 break;
148 case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE: 237 case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE:
149 change_char = '-'; 238 modify_type = MODIFY_REMOVE;
150 break; 239 break;
151 default: 240 default:
152 i_unreached(); 241 i_unreached();
153 } 242 }
154 243
155 str_printfa(str, "UID STORE %u:%u %cFLAGS (",
156 sync_rec->uid1, sync_rec->uid2, change_char);
157
158 kw_p = array_idx(ctx->keywords, sync_rec->keyword_idx); 244 kw_p = array_idx(ctx->keywords, sync_rec->keyword_idx);
159 str_append(str, *kw_p); 245 imapc_sync_store(ctx, modify_type, sync_rec->uid1,
160 str_append_c(str, ')'); 246 sync_rec->uid2, *kw_p);
161 imapc_sync_store_cmd(ctx, str_c(str));
162 } 247 }
163 248
164 static void imapc_sync_expunge_finish(struct imapc_sync_context *ctx) 249 static void imapc_sync_expunge_finish(struct imapc_sync_context *ctx)
165 { 250 {
166 string_t *str; 251 string_t *str;
357 struct mail_index_sync_rec sync_rec; 442 struct mail_index_sync_rec sync_rec;
358 uint32_t seq1, seq2; 443 uint32_t seq1, seq2;
359 444
360 i_array_init(&ctx->expunged_uids, 64); 445 i_array_init(&ctx->expunged_uids, 64);
361 ctx->keywords = mail_index_get_keywords(mbox->box.index); 446 ctx->keywords = mail_index_get_keywords(mbox->box.index);
447 ctx->pool = pool_alloconly_create("imapc sync pool", 1024);
362 448
363 imapc_sync_uid_validity(ctx); 449 imapc_sync_uid_validity(ctx);
364 while (mail_index_sync_next(ctx->index_sync_ctx, &sync_rec)) T_BEGIN { 450 while (mail_index_sync_next(ctx->index_sync_ctx, &sync_rec)) T_BEGIN {
365 if (!mail_index_lookup_seq_range(ctx->sync_view, 451 if (!mail_index_lookup_seq_range(ctx->sync_view,
366 sync_rec.uid1, sync_rec.uid2, 452 sync_rec.uid1, sync_rec.uid2,
379 case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE: 465 case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE:
380 imapc_sync_index_keyword(ctx, &sync_rec); 466 imapc_sync_index_keyword(ctx, &sync_rec);
381 break; 467 break;
382 } 468 }
383 } T_END; 469 } T_END;
470 imapc_sync_finish_store(ctx);
471 pool_unref(&ctx->pool);
384 472
385 if (!mbox->initial_sync_done) { 473 if (!mbox->initial_sync_done) {
386 /* with initial syncing we're fetching all messages' flags and 474 /* with initial syncing we're fetching all messages' flags and
387 expunge mails from local index that no longer exist on 475 expunge mails from local index that no longer exist on
388 remote server */ 476 remote server */