libindex_a_SOURCES = \
mail-cache.c \
+ mail-cache-compress.c \
+ mail-cache-decisions.c \
mail-cache-lookup.c \
mail-cache-transaction.c \
mail-index.c \
+/* Copyright (C) 2003-2004 Timo Sirainen */
+
+#include "lib.h"
+#include "buffer.h"
+#include "byteorder.h"
+#include "ostream.h"
+#include "mail-cache-private.h"
+
+static unsigned char null4[4] = { 0, 0, 0, 0 };
+
static const struct mail_cache_record *
-mail_cache_compress_record(struct mail_cache *cache,
- struct mail_index_record *rec, int header_idx,
- uint32_t *size_r)
+mail_cache_compress_record(struct mail_cache_view *view, uint32_t seq,
+ enum mail_cache_field orig_cached_fields,
+ int header_idx, uint32_t *size_r)
{
- enum mail_cache_field orig_cached_fields, cached_fields, field;
+ enum mail_cache_field cached_fields, field;
struct mail_cache_record cache_rec;
buffer_t *buffer;
const void *data;
buffer = buffer_create_dynamic(pool_datastack_create(),
4096, (size_t)-1);
- orig_cached_fields = mail_cache_get_fields(cache, rec);
cached_fields = orig_cached_fields & ~MAIL_CACHE_HEADERS_MASK;
buffer_append(buffer, &cache_rec, sizeof(cache_rec));
for (i = 0, field = 1; i < 31; i++, field <<= 1) {
if ((cached_fields & field) == 0)
continue;
- if (!mail_cache_lookup_field(cache, rec, field, &data, &size)) {
+ if (!mail_cache_lookup_field(view, seq, field, &data, &size)) {
cached_fields &= ~field;
continue;
}
for (i = 0; i <= header_idx; i++) {
field = mail_cache_header_fields[i];
- if (mail_cache_lookup_field(cache, rec, field,
+ if (mail_cache_lookup_field(view, seq, field,
&data, &size) && size > 1) {
size--; /* terminating \0 */
buffer_append(buffer, data, size);
nb_size += size;
}
}
- buffer_append(buffer, "", 1);
+ buffer_append(buffer, null4, 1);
nb_size++;
if ((nb_size & 3) != 0)
buffer_append(buffer, null4, 4 - (nb_size & 3));
return data;
}
-static int mail_cache_copy(struct mail_cache *cache, int fd)
+static int
+mail_cache_copy(struct mail_cache *cache, struct mail_index_view *view, int fd)
{
-#if 0
- struct mail_cache_header *hdr;
+ struct mail_cache_view *cache_view;
+ struct mail_index_transaction *t;
+ const struct mail_index_header *idx_hdr;
const struct mail_cache_record *cache_rec;
- struct mail_index_record *rec;
- enum mail_cache_field used_fields;
- unsigned char *mmap_base;
+ struct mail_cache_header hdr;
+ struct ostream *output;
+ enum mail_cache_field keep_fields, temp_fields;
+ enum mail_cache_field cached_fields, new_fields;
const char *str;
- uint32_t new_file_size, offset, size, nb_size;
- int i, header_idx;
-
- /* pick some reasonably good file size */
- new_file_size = cache->used_file_size -
- nbo_to_uint32(cache->hdr->deleted_space);
- new_file_size = (new_file_size + 1023) & ~1023;
- if (new_file_size < MAIL_CACHE_INITIAL_SIZE)
- new_file_size = MAIL_CACHE_INITIAL_SIZE;
-
- if (file_set_size(fd, new_file_size) < 0)
- return mail_cache_set_syscall_error(cache, "file_set_size()");
-
- mmap_base = mmap(NULL, new_file_size, PROT_READ | PROT_WRITE,
- MAP_SHARED, fd, 0);
- if (mmap_base == MAP_FAILED)
- return mail_cache_set_syscall_error(cache, "mmap()");
+ uint32_t size, nb_size, message_count, seq, first_new_seq;
+ uoff_t offset;
+ int i, header_idx, ret;
+
+ /* get sequence of first message which doesn't need it's temp fields
+ removed. */
+ if (mail_index_get_header(view, &idx_hdr) < 0)
+ return -1;
+ if (mail_index_lookup_uid_range(view, idx_hdr->day_first_uid[7],
+ (uint32_t)-1, &first_new_seq,
+ &message_count) < 0)
+ return -1;
+ if (first_new_seq == 0)
+ first_new_seq = message_count+1;
+
+ cache_view = mail_cache_view_open(cache, view);
+ t = mail_index_transaction_begin(view, FALSE);
+ output = o_stream_create_file(fd, default_pool, 0, FALSE);
+
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.indexid = cache->hdr->indexid;
+ hdr.file_seq = cache->hdr->file_seq + 1;
+
+ memcpy(hdr.field_usage_decision_type,
+ cache->hdr->field_usage_decision_type,
+ sizeof(hdr.field_usage_decision_type));
+ memcpy(hdr.field_usage_last_used,
+ cache->hdr->field_usage_last_used,
+ sizeof(hdr.field_usage_last_used));
+
+ keep_fields = temp_fields = 0;
+ for (i = 0; i < 32; i++) {
+ if (cache->hdr->field_usage_decision_type[i] &
+ MAIL_CACHE_DECISION_YES)
+ keep_fields |= 1 << i;
+ else if (cache->hdr->field_usage_decision_type[i] &
+ MAIL_CACHE_DECISION_TEMP)
+ temp_fields |= 1 << i;
+ }
- /* skip file's header */
- hdr = (struct mail_cache_header *) mmap_base;
- offset = sizeof(*hdr);
+ offset = sizeof(hdr);
/* merge all the header pieces into one. if some message doesn't have
all the required pieces, we'll just have to drop them all. */
if (str == NULL)
header_idx = -1;
else {
- hdr->header_offsets[0] = uint32_to_offset(offset);
+ hdr.header_offsets[0] = mail_cache_uint32_to_offset(offset);
header_idx = i;
size = strlen(str) + 1;
nb_size = uint32_to_nbo(size);
- memcpy(mmap_base + offset, &nb_size, sizeof(nb_size));
- offset += sizeof(nb_size);
- memcpy(mmap_base + offset, str, size);
- offset += (size + 3) & ~3;
+ o_stream_send(output, &nb_size, sizeof(nb_size));
+ o_stream_send(output, str, size);
+ if ((size & 3) != 0)
+ o_stream_send(output, null4, 4 - (size & 3));
}
- // FIXME: recreate index file with new cache_offsets
+ mail_index_reset_cache(t, hdr.file_seq);
- used_fields = 0;
- rec = cache->index->lookup(cache->index, 1);
- while (rec != NULL) {
- cache_rec = mail_cache_lookup(cache, rec, 0);
+ ret = 0;
+ for (seq = 1; seq <= message_count; seq++) {
+ cache_rec = mail_cache_lookup(cache_view, seq, 0);
if (cache_rec == NULL)
- rec->cache_offset = 0;
- else if (offset_to_uint32(cache_rec->next_offset) == 0) {
- /* just one unmodified block, copy it */
- size = nbo_to_uint32(cache_rec->size);
- i_assert(offset + size <= new_file_size);
+ continue;
+
+ cached_fields = mail_cache_get_fields(cache_view, seq);
+ new_fields = cached_fields & keep_fields;
+ if ((cached_fields & temp_fields) != 0 &&
+ seq >= first_new_seq) {
+ /* new message, keep temp fields */
+ new_fields |= cached_fields & temp_fields;
+ }
- memcpy(mmap_base + offset, cache_rec, size);
- rec->cache_offset = uint32_to_offset(offset);
+ if (keep_fields == cached_fields &&
+ mail_cache_offset_to_uint32(cache_rec->next_offset) == 0) {
+ /* just one unmodified block, save it */
+ size = nbo_to_uint32(cache_rec->size);
+ mail_index_update_cache(t, seq, output->offset);
+ o_stream_send(output, cache_rec, size);
- size = (size + 3) & ~3;
- offset += size;
+ if ((size & 3) != 0)
+ o_stream_send(output, null4, 4 - (size & 3));
} else {
- /* multiple blocks, sort them into buffer */
+ /* a) dropping fields
+ b) multiple blocks, sort them into buffer */
+ mail_index_update_cache(t, seq, output->offset);
+
t_push();
- cache_rec = mail_cache_compress_record(cache, rec,
+ cache_rec = mail_cache_compress_record(cache_view, seq,
+ keep_fields,
header_idx,
&size);
- i_assert(offset + size <= new_file_size);
- memcpy(mmap_base + offset, cache_rec, size);
- used_fields |= cache_rec->fields;
+ o_stream_send(output, cache_rec, size);
t_pop();
-
- rec->cache_offset = uint32_to_offset(offset);
- offset += size;
}
-
- rec = cache->index->next(cache->index, rec);
}
+ hdr.used_file_size = uint32_to_nbo(output->offset);
- /* update header */
- hdr->indexid = cache->index->indexid;
- hdr->file_seq = cache->index->hdr->cache_sync_id+1;
- hdr->used_file_size = uint32_to_nbo(offset);
- hdr->used_fields = used_fields;
- hdr->field_usage_start = uint32_to_nbo(ioloop_time);
+ o_stream_unref(output);
+ mail_cache_view_close(cache_view);
- /* write everything to disk */
- if (msync(mmap_base, offset, MS_SYNC) < 0)
- return mail_cache_set_syscall_error(cache, "msync()");
-
- if (munmap(mmap_base, new_file_size) < 0)
- return mail_cache_set_syscall_error(cache, "munmap()");
+ if (fdatasync(fd) < 0) {
+ mail_cache_set_syscall_error(cache, "fdatasync()");
+ (void)mail_index_transaction_rollback(t);
+ return -1;
+ }
- if (fdatasync(fd) < 0)
- return mail_cache_set_syscall_error(cache, "fdatasync()");
- return TRUE;
-#endif
+ return mail_index_transaction_commit(t, &seq, &offset);
}
-int mail_cache_compress(struct mail_cache *cache)
+int mail_cache_compress(struct mail_cache *cache, struct mail_index_view *view)
{
- int fd, ret = TRUE;
+ int fd, ret;
i_assert(cache->trans_ctx == NULL);
- if (cache->anon_mmap)
- return TRUE;
-
- if (!cache->index->set_lock(cache->index, MAIL_LOCK_EXCLUSIVE))
- return FALSE;
-
- if (mail_cache_lock(cache, TRUE) <= 0)
- return FALSE;
+ if ((ret = mail_cache_lock(cache, TRUE)) <= 0)
+ return ret;
#ifdef DEBUG
i_warning("Compressing cache file %s", cache->filepath);
#endif
- fd = file_dotlock_open(cache->filepath, NULL, MAIL_CACHE_LOCK_TIMEOUT,
+ fd = file_dotlock_open(cache->filepath, NULL, NULL,
+ MAIL_CACHE_LOCK_TIMEOUT,
MAIL_CACHE_LOCK_CHANGE_TIMEOUT,
MAIL_CACHE_LOCK_IMMEDIATE_TIMEOUT, NULL, NULL);
if (fd == -1) {
mail_cache_set_syscall_error(cache, "file_dotlock_open()");
- return FALSE;
+ return -1;
}
- /* now we'll begin the actual moving. keep rebuild-flag on
- while doing it. */
- cache->index->hdr->flags |= MAIL_INDEX_HDR_FLAG_REBUILD;
- if (!mail_index_fmdatasync(cache->index, cache->index->hdr_size))
- return FALSE;
-
- if (!mail_cache_copy(cache, fd)) {
- (void)file_dotlock_delete(cache->filepath, fd);
- ret = FALSE;
+ if (mail_cache_copy(cache, view, fd) < 0) {
+ (void)file_dotlock_delete(cache->filepath, NULL, fd);
+ ret = -1;
} else {
- mail_cache_file_close(cache);
- cache->fd = dup(fd);
-
- if (file_dotlock_replace(cache->filepath, fd, FALSE) < 0) {
+ if (file_dotlock_replace(cache->filepath, NULL,
+ -1, FALSE) < 0) {
mail_cache_set_syscall_error(cache,
"file_dotlock_replace()");
- ret = FALSE;
- }
+ (void)close(fd);
+ ret = -1;
+ } else {
+ mail_cache_file_close(cache);
+ cache->fd = fd;
- if (!mmap_update(cache, 0, 0))
- ret = FALSE;
+ if (mail_cache_mmap_update(cache, 0, 0) < 0)
+ ret = -1;
+ }
}
/* headers could have changed, reread them */
memset(cache->split_offsets, 0, sizeof(cache->split_offsets));
memset(cache->split_headers, 0, sizeof(cache->split_headers));
- if (ret) {
- cache->index->hdr->flags &=
- ~(MAIL_INDEX_HDR_FLAG_REBUILD |
- MAIL_INDEX_HDR_FLAG_COMPRESS_CACHE);
- }
-
if (mail_cache_unlock(cache) < 0)
- ret = FALSE;
+ return -1;
+ if (ret == 0)
+ cache->need_compress = FALSE;
return ret;
}
+
+int mail_cache_need_compress(struct mail_cache *cache)
+{
+ return cache->need_compress;
+}
--- /dev/null
+/* Copyright (C) 2004 Timo Sirainen */
+
+#include "lib.h"
+#include "write-full.h"
+#include "mail-cache-private.h"
+
+#include <stddef.h>
+
+static void
+mail_cache_set_decision_type(struct mail_cache *cache, uint32_t idx,
+ enum mail_cache_decision_type type)
+{
+ uint8_t value = type;
+
+ /* update the header without locking, we'll just write one byte and
+ it's very unlikely someone else tries to write different value for
+ it at the same time. even then it's just a wrong decision which
+ will be corrected sometimes later, not too bad.. */
+ if (pwrite_full(cache->fd, &value, 1,
+ offsetof(struct mail_cache_header,
+ field_usage_decision_type) + idx) < 0) {
+ mail_cache_set_syscall_error(cache, "pwrite_full()");
+ }
+}
+
+void mail_cache_handle_decisions(struct mail_cache_view *view, uint32_t seq,
+ enum mail_cache_field field)
+{
+ const struct mail_index_header *hdr;
+ unsigned int idx;
+ uint32_t uid;
+
+ idx = mail_cache_field_index(field);
+ if (view->cache->hdr->field_usage_decision_type[idx] !=
+ MAIL_CACHE_DECISION_TEMP) {
+ /* a) forced decision
+ b) not cached, mail_cache_mark_missing() will handle this
+ c) permanently cached already, okay. */
+ return;
+ }
+
+ /* see if we want to change decision from TEMP to YES */
+ if (mail_index_lookup_uid(view->view, seq, &uid) < 0 ||
+ mail_index_get_header(view->view, &hdr) < 0)
+ return;
+
+ if (uid < view->cache->field_usage_uid_highwater[idx] ||
+ uid < hdr->day_first_uid[7]) {
+ /* a) nonordered access within this session. if client doesn't
+ request messages in growing order, we assume it doesn't
+ have a permanent local cache.
+ b) accessing message older than one week. assume it's a
+ client with no local cache. if it was just a new client
+ generating the local cache for the first time, we'll
+ drop back to TEMP within few months. */
+ mail_cache_set_decision_type(view->cache, idx,
+ MAIL_CACHE_DECISION_YES);
+ } else {
+ view->cache->field_usage_uid_highwater[idx] = uid;
+ }
+}
+
+void mail_cache_mark_missing(struct mail_cache_view *view, uint32_t seq,
+ enum mail_cache_field field)
+{
+ unsigned int idx;
+ uint32_t uid;
+
+ idx = mail_cache_field_index(field);
+ if (view->cache->hdr->field_usage_decision_type[idx] !=
+ MAIL_CACHE_DECISION_NO) {
+ /* a) forced decision
+ b) we're already caching it, so it just wasn't in cache */
+ return;
+ }
+
+ /* field used the first time */
+ mail_cache_set_decision_type(view->cache, idx,
+ MAIL_CACHE_DECISION_TEMP);
+
+ if (mail_index_lookup_uid(view->view, seq, &uid) == 0)
+ view->cache->field_usage_uid_highwater[idx] = uid;
+}
static int cache_get_field(struct mail_cache *cache,
struct mail_cache_record *cache_rec,
enum mail_cache_field field,
- void **data_r, size_t *size_r)
+ const void **data_r, size_t *size_r)
{
unsigned char *buf;
unsigned int mask;
return FALSE;
}
-static int cache_lookup_field(struct mail_cache_view *view, uint32_t seq,
- enum mail_cache_field field,
- void **data_r, size_t *size_r)
+int mail_cache_lookup_field(struct mail_cache_view *view, uint32_t seq,
+ enum mail_cache_field field,
+ const void **data_r, size_t *size_r)
{
struct mail_cache_record *cache_rec;
+ mail_cache_handle_decisions(view, seq, field);
+
cache_rec = mail_cache_lookup(view, seq, field);
while (cache_rec != NULL) {
if ((cache_rec->fields & field) != 0) {
return FALSE;
}
-int mail_cache_lookup_field(struct mail_cache_view *view, uint32_t seq,
- enum mail_cache_field field,
- const void **data_r, size_t *size_r)
-{
- void *data;
-
- if (!cache_lookup_field(view, seq, field, &data, size_r))
- return FALSE;
-
- *data_r = data;
- return TRUE;
-}
-
const char *
mail_cache_lookup_string_field(struct mail_cache_view *view, uint32_t seq,
enum mail_cache_field field)
#define CACHE_RECORD(cache, offset) \
((struct mail_cache_record *) ((char *) (cache)->mmap_base + offset))
+enum mail_cache_decision_type {
+ /* Not needed currently */
+ MAIL_CACHE_DECISION_NO = 0x00,
+ /* Needed only for new mails. Drop when compressing. */
+ MAIL_CACHE_DECISION_TEMP = 0x01,
+ /* Needed. */
+ MAIL_CACHE_DECISION_YES = 0x02,
+
+ /* This decision has been forced manually, don't change it. */
+ MAIL_CACHE_DECISION_FORCED = 0x80
+};
+
struct mail_cache_header {
uint32_t indexid;
uint32_t file_seq;
uint32_t used_file_size;
uint32_t deleted_space;
- uint32_t used_fields; /* enum mail_cache_field */
-
- uint32_t field_usage_start; /* time_t */
- uint32_t field_usage_counts[32];
+ uint32_t field_usage_last_used[32]; /* time_t */
+ uint8_t field_usage_decision_type[32];
uint32_t header_offsets[MAIL_CACHE_HEADERS_COUNT];
};
enum mail_cache_field default_cache_fields;
enum mail_cache_field never_cache_fields;
+ uint32_t field_usage_uid_highwater[32];
+
struct mail_cache_transaction_ctx *trans_ctx;
unsigned int locks;
unsigned int mmap_refresh:1;
+ unsigned int need_compress:1;
unsigned int silent:1;
unsigned int disabled:1;
};
uint32_t mail_cache_uint32_to_offset(uint32_t offset);
uint32_t mail_cache_offset_to_uint32(uint32_t offset);
+unsigned int mail_cache_field_index(enum mail_cache_field field);
const char *
mail_cache_get_header_fields_str(struct mail_cache *cache, unsigned int idx);
int mail_cache_mmap_update(struct mail_cache *cache,
size_t offset, size_t size);
+void mail_cache_file_close(struct mail_cache *cache);
+
+void mail_cache_handle_decisions(struct mail_cache_view *view, uint32_t seq,
+ enum mail_cache_field field);
void mail_cache_set_syscall_error(struct mail_cache *cache,
const char *function);
COMPRESS_CONTINUED_PERCENTAGE &&
ctx->used_file_size >= COMPRESS_MIN_SIZE) {
/* too many continued rows, compress */
- //FIXME:cache->index->set_flags |= MAIL_INDEX_HDR_FLAG_COMPRESS_CACHE;
+ cache->need_compress = TRUE;
}
cache->hdr->continued_record_count = uint32_to_nbo(cont);
if (ctx->next_unused_header_lowwater == MAIL_CACHE_HEADERS_COUNT) {
/* they're all used - compress the cache to get more */
- /* FIXME: ctx->cache->index->set_flags |=
- MAIL_INDEX_HDR_FLAG_COMPRESS_CACHE;*/
+ ctx->cache->need_compress = TRUE;
}
mail_cache_transaction_flush(ctx);
/* see if we've reached the max. deleted space in file */
max_del_space = ctx->used_file_size / 100 * COMPRESS_PERCENTAGE;
if (deleted_space >= max_del_space &&
- ctx->used_file_size >= COMPRESS_MIN_SIZE) {
- //FIXME:cache->index->set_flags |= MAIL_INDEX_HDR_FLAG_COMPRESS_CACHE;
- }
+ ctx->used_file_size >= COMPRESS_MIN_SIZE)
+ cache->need_compress = TRUE;
cache->hdr->deleted_space = uint32_to_nbo(deleted_space);
return 0;
/* Copyright (C) 2003-2004 Timo Sirainen */
#include "lib.h"
-#include "buffer.h"
#include "byteorder.h"
#include "file-lock.h"
#include "file-set-size.h"
#include "write-full.h"
#include "mail-cache-private.h"
-#include <stddef.h>
#include <unistd.h>
#include <sys/stat.h>
(((uint32_t)buf[0] & 0x7f) << 23);
}
+unsigned int mail_cache_field_index(enum mail_cache_field field)
+{
+ unsigned int i, num;
+
+ for (i = 0, num = 1; i < 32; i++, num <<= 1) {
+ if (field == num)
+ return i;
+ }
+ i_unreached();
+}
+
void mail_cache_set_syscall_error(struct mail_cache *cache,
const char *function)
{
va_end(va);
}
-static void mail_cache_file_close(struct mail_cache *cache)
+void mail_cache_file_close(struct mail_cache *cache)
{
if (cache->mmap_base != NULL) {
if (munmap(cache->mmap_base, cache->mmap_length) < 0)
{
i_free(view);
}
-
-void mail_cache_mark_missing(struct mail_cache_view *view,
- enum mail_cache_field fields)
-{
- // FIXME
-}
enum mail_cache_field {
/* fixed size fields */
MAIL_CACHE_INDEX_FLAGS = 0x00000001,
- MAIL_CACHE_SENT_DATE = 0x00000008,
- MAIL_CACHE_RECEIVED_DATE = 0x00000010,
- MAIL_CACHE_VIRTUAL_FULL_SIZE = 0x00000020,
+ MAIL_CACHE_SENT_DATE = 0x00000002,
+ MAIL_CACHE_RECEIVED_DATE = 0x00000004,
+ MAIL_CACHE_VIRTUAL_FULL_SIZE = 0x00000008,
/* variable sized field */
MAIL_CACHE_HEADERS1 = 0x40000000,
enum mail_cache_field default_cache_fields,
enum mail_cache_field never_cache_fields);
+/* Returns TRUE if cache should be compressed. */
+int mail_cache_need_compress(struct mail_cache *cache);
/* Compress cache file. */
-int mail_cache_compress(struct mail_cache *cache);
+int mail_cache_compress(struct mail_cache *cache, struct mail_index_view *view);
/* Reset the cache file, clearing all data. */
int mail_cache_reset(struct mail_cache *cache);
enum mail_cache_field field,
void *buffer, size_t buffer_size);
-/* Mark given fields as missing, ie. they should be cached when possible. */
-void mail_cache_mark_missing(struct mail_cache_view *view,
- enum mail_cache_field fields);
+/* Mark given field as missing, ie. it should be cached when possible. */
+void mail_cache_mark_missing(struct mail_cache_view *view, uint32_t uid,
+ enum mail_cache_field field);
/* Return record flags. */
enum mail_cache_record_flag
/* Copyright (C) 2004 Timo Sirainen */
#include "lib.h"
+#include "ioloop.h"
#include "buffer.h"
#include "file-set-size.h"
#include "mmap-util.h"
#include "mail-transaction-log.h"
#include "mail-transaction-util.h"
+#include <time.h>
+
static void
mail_index_header_update_counts(struct mail_index_header *hdr,
uint8_t old_flags, uint8_t new_flags)
map->write_to_disk = TRUE;
}
+static void
+mail_index_update_day_headers(struct mail_index_header *hdr, uint32_t uid)
+{
+ const int max_days =
+ sizeof(hdr->day_first_uid) / sizeof(hdr->day_first_uid[0]);
+ struct tm tm;
+ time_t stamp;
+ int i, days;
+
+ /* get beginning of today */
+ tm = *localtime(&ioloop_time);
+ tm.tm_hour = 0;
+ tm.tm_min = 0;
+ tm.tm_sec = 0;
+ stamp = mktime(&tm);
+ if (stamp == (time_t)-1)
+ i_panic("mktime(today) failed");
+
+ if ((time_t)hdr->day_stamp >= stamp)
+ return;
+
+ /* get number of days since last message */
+ days = (stamp - hdr->day_stamp) / (3600*24);
+ if (days > max_days)
+ days = max_days;
+
+ /* @UNSAFE: move days forward and fill the missing days with old
+ day_first_uid[0]. */
+ memcpy(hdr->day_first_uid + days,
+ hdr->day_first_uid, max_days - days);
+ for (i = 1; i < days; i++)
+ hdr->day_first_uid[i] = hdr->day_first_uid[0];
+
+ hdr->day_stamp = stamp;
+ hdr->day_first_uid[0] = uid;
+}
+
int mail_index_sync_update_index(struct mail_index_sync_ctx *sync_ctx)
{
struct mail_index *index = sync_ctx->index;
const struct mail_transaction_header *hdr;
const void *data;
unsigned int count, old_lock_id;
- uint32_t seq, i;
+ uint32_t seq, i, first_append_uid;
uoff_t offset;
int ret, had_dirty, skipped;
view->map = map;
view->map->refcount++;
+ first_append_uid = 0;
had_dirty = (map->hdr_copy.flags & MAIL_INDEX_HDR_FLAG_HAVE_DIRTY) != 0;
if (had_dirty)
map->hdr_copy.flags &= ~MAIL_INDEX_HDR_FLAG_HAVE_DIRTY;
if ((hdr->type & MAIL_TRANSACTION_APPEND) != 0) {
const struct mail_transaction_append_header *append_hdr;
+ const struct mail_index_record *rec;
+
+ rec = CONST_PTR_OFFSET(data, sizeof(*append_hdr));
+ if (first_append_uid == 0)
+ first_append_uid = rec->uid;
append_hdr = data;
if (append_hdr->record_size > map->hdr->record_size) {
mail_index_sync_replace_map(view, map);
}
count = (hdr->size - sizeof(*append_hdr)) /
- append_hdr->record_size;
+ append_hdr->record_size;
if (mail_index_grow(index, view->map, count) < 0) {
ret = -1;
break;
}
if (ret < 0) {
- /* */
mail_index_view_unlock(view);
return -1;
}
map->hdr_copy.log_file_seq = seq;
map->hdr_copy.log_file_offset = offset;
+ if (first_append_uid != 0)
+ mail_index_update_day_headers(&map->hdr_copy, first_append_uid);
+
if ((map->hdr_copy.flags & MAIL_INDEX_HDR_FLAG_HAVE_DIRTY) == 0 &&
had_dirty) {
/* do we have dirty flags anymore? */
int mail_index_sync_end(struct mail_index_sync_ctx *ctx)
{
const struct mail_index_header *hdr;
- uint32_t seq;
- uoff_t offset;
+ uint32_t seq, seq2;
+ uoff_t offset, offset2;
int ret = 0;
if (mail_transaction_log_view_is_corrupted(ctx->view->log_view))
hdr->log_file_seq, hdr->log_file_offset,
seq, offset, MAIL_TRANSACTION_TYPE_MASK) < 0)
ret = -1;
- if (mail_index_sync_update_index(ctx) < 0)
+ else if (mail_index_sync_update_index(ctx) < 0)
ret = -1;
}
+ if (ret == 0 && mail_cache_need_compress(ctx->index->cache)) {
+ if (mail_cache_compress(ctx->index->cache, ctx->view) < 0)
+ ret = -1;
+ else {
+ /* cache_offsets have changed, sync them */
+ mail_transaction_log_get_head(ctx->index->log,
+ &seq2, &offset2);
+ if (mail_transaction_log_view_set(ctx->view->log_view,
+ seq, offset, seq2, offset2,
+ MAIL_TRANSACTION_TYPE_MASK) < 0)
+ ret = -1;
+ else if (mail_index_sync_update_index(ctx) < 0)
+ ret = -1;
+ }
+ }
+
mail_index_unlock(ctx->index, ctx->lock_id);
i_assert(!ctx->index->map->write_to_disk);
mail_transaction_log_sync_unlock(ctx->index->log);
#define MAIL_INDEX_MAJOR_VERSION 4
#define MAIL_INDEX_MINOR_VERSION 0
-#define MAIL_INDEX_HEADER_MIN_SIZE 88
+#define MAIL_INDEX_HEADER_MIN_SIZE 120
/* Number of keywords in mail_index_record. */
#define INDEX_KEYWORDS_COUNT (3*8)
uint32_t log_file_seq;
uint32_t log_file_offset;
- uint64_t sync_size;
uint32_t sync_stamp;
+ uint64_t sync_size;
uint32_t cache_file_seq;
uint32_t extra_records_hdr_offset;
+
+ /* daily first UIDs that have been added to index. */
+ uint32_t day_stamp;
+ uint32_t day_first_uid[8];
};
struct mail_index_record {
size_t part_size;
if ((mail->data.cached_fields & MAIL_CACHE_MESSAGEPART) == 0) {
- mail_cache_mark_missing(mail->trans->cache_view,
+ mail_cache_mark_missing(mail->trans->cache_view, mail->data.seq,
MAIL_CACHE_MESSAGEPART);
return NULL;
}
const char *ret;
if ((mail->data.cached_fields & field) == 0) {
- mail_cache_mark_missing(mail->trans->cache_view, field);
+ mail_cache_mark_missing(mail->trans->cache_view,
+ mail->data.seq, field);
return NULL;
}
if (!mail_cache_copy_fixed_field(mail->trans->cache_view,
mail->data.seq, field,
&uoff, sizeof(uoff))) {
- mail_cache_mark_missing(mail->trans->cache_view, field);
+ mail_cache_mark_missing(mail->trans->cache_view,
+ mail->data.seq, field);
uoff = (uoff_t)-1;
}
mail->data.seq,
MAIL_CACHE_RECEIVED_DATE,
&t, sizeof(t))) {
- mail_cache_mark_missing(mail->trans->cache_view,
+ mail_cache_mark_missing(mail->trans->cache_view, mail->data.seq,
MAIL_CACHE_RECEIVED_DATE);
t = (time_t)-1;
}
mail->data.seq,
MAIL_CACHE_SENT_DATE,
sent_date, sizeof(*sent_date))) {
- mail_cache_mark_missing(mail->trans->cache_view,
+ mail_cache_mark_missing(mail->trans->cache_view, mail->data.seq,
MAIL_CACHE_SENT_DATE);
sent_date->time = (time_t)-1;