lib: cache configuration information from database

The main goal is to allow configuration information to be temporarily
overridden by a separate config file. That will require further
changes not in this commit.

The performance impact is unclear, and will depend on the balance
between number of queries and number of distinct metadata items read
on the first call to n_d_get_config.
This commit is contained in:
David Bremner 2020-08-08 11:16:37 -03:00
parent 763445beae
commit 4743e87c2c
6 changed files with 75 additions and 21 deletions

View file

@ -50,6 +50,11 @@ notmuch_database_set_config (notmuch_database_t *notmuch,
if (status) if (status)
return status; return status;
if (! notmuch->config) {
if ((status = _notmuch_config_load_from_database (notmuch)))
return status;
}
try { try {
notmuch->writable_xapian_db->set_metadata (CONFIG_PREFIX + key, value); notmuch->writable_xapian_db->set_metadata (CONFIG_PREFIX + key, value);
} catch (const Xapian::Error &error) { } catch (const Xapian::Error &error) {
@ -58,7 +63,13 @@ notmuch_database_set_config (notmuch_database_t *notmuch,
_notmuch_database_log (notmuch, "Error: A Xapian exception occurred setting metadata: %s\n", _notmuch_database_log (notmuch, "Error: A Xapian exception occurred setting metadata: %s\n",
error.get_msg ().c_str ()); error.get_msg ().c_str ());
} }
return status;
if (status)
return status;
_notmuch_string_map_set (notmuch->config, key, value);
return NOTMUCH_STATUS_SUCCESS;
} }
static notmuch_status_t static notmuch_status_t
@ -84,17 +95,25 @@ notmuch_database_get_config (notmuch_database_t *notmuch,
const char *key, const char *key,
char **value) char **value)
{ {
std::string strval; const char* stored_val;
notmuch_status_t status; notmuch_status_t status;
if (! notmuch->config) {
if ((status = _notmuch_config_load_from_database (notmuch)))
return status;
}
if (! value) if (! value)
return NOTMUCH_STATUS_NULL_POINTER; return NOTMUCH_STATUS_NULL_POINTER;
status = _metadata_value (notmuch, key, strval); stored_val = _notmuch_string_map_get (notmuch->config, key);
if (status) if (! stored_val) {
return status; /* XXX in principle this API should be fixed so empty string
* is distinguished from not found */
*value = strdup (strval.c_str ()); *value = strdup("");
} else {
*value = strdup (stored_val);
}
return NOTMUCH_STATUS_SUCCESS; return NOTMUCH_STATUS_SUCCESS;
} }
@ -201,3 +220,28 @@ notmuch_config_list_destroy (notmuch_config_list_t *list)
{ {
talloc_free (list); talloc_free (list);
} }
notmuch_status_t
_notmuch_config_load_from_database (notmuch_database_t *notmuch)
{
notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
notmuch_config_list_t *list;
if (notmuch->config == NULL)
notmuch->config = _notmuch_string_map_create (notmuch);
if (unlikely(notmuch->config == NULL))
return NOTMUCH_STATUS_OUT_OF_MEMORY;
status = notmuch_database_get_config_list (notmuch, "", &list);
if (status)
return status;
for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) {
_notmuch_string_map_append (notmuch->config,
notmuch_config_list_key (list),
notmuch_config_list_value (list));
}
return status;
}

View file

@ -228,6 +228,9 @@ struct _notmuch_database {
* here, but at least they are small */ * here, but at least they are small */
notmuch_string_map_t *user_prefix; notmuch_string_map_t *user_prefix;
notmuch_string_map_t *user_header; notmuch_string_map_t *user_header;
/* Cached and possibly overridden configuration */
notmuch_string_map_t *config;
}; };
/* Prior to database version 3, features were implied by the database /* Prior to database version 3, features were implied by the database

View file

@ -704,6 +704,9 @@ struct _notmuch_indexopts {
#define EMPTY_STRING(s) ((s)[0] == '\0') #define EMPTY_STRING(s) ((s)[0] == '\0')
/* config.cc */
notmuch_status_t
_notmuch_config_load_from_database (notmuch_database_t * db);
NOTMUCH_END_DECLS NOTMUCH_END_DECLS
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -90,7 +90,7 @@ notmuch_database_open_verbose (const char *path,
notmuch->exception_reported = false; notmuch->exception_reported = false;
notmuch->status_string = NULL; notmuch->status_string = NULL;
notmuch->path = talloc_strdup (notmuch, path); notmuch->path = talloc_strdup (notmuch, path);
notmuch->config = NULL;
strip_trailing (notmuch->path, '/'); strip_trailing (notmuch->path, '/');
notmuch->writable_xapian_db = NULL; notmuch->writable_xapian_db = NULL;
@ -180,6 +180,11 @@ notmuch_database_open_verbose (const char *path,
notmuch->query_parser->add_rangeprocessor (notmuch->date_range_processor); notmuch->query_parser->add_rangeprocessor (notmuch->date_range_processor);
notmuch->query_parser->add_rangeprocessor (notmuch->last_mod_range_processor); notmuch->query_parser->add_rangeprocessor (notmuch->last_mod_range_processor);
/* Configuration information is needed to set up query parser */
status = _notmuch_config_load_from_database (notmuch);
if (status)
goto DONE;
status = _notmuch_database_setup_standard_query_fields (notmuch); status = _notmuch_database_setup_standard_query_fields (notmuch);
if (status) if (status)
goto DONE; goto DONE;

View file

@ -166,8 +166,7 @@ _notmuch_database_setup_standard_query_fields (notmuch_database_t *notmuch)
notmuch_status_t notmuch_status_t
_notmuch_database_setup_user_query_fields (notmuch_database_t *notmuch) _notmuch_database_setup_user_query_fields (notmuch_database_t *notmuch)
{ {
notmuch_config_list_t *list; notmuch_string_map_iterator_t *list;
notmuch_status_t status;
notmuch->user_prefix = _notmuch_string_map_create (notmuch); notmuch->user_prefix = _notmuch_string_map_create (notmuch);
if (notmuch->user_prefix == NULL) if (notmuch->user_prefix == NULL)
@ -177,15 +176,16 @@ _notmuch_database_setup_user_query_fields (notmuch_database_t *notmuch)
if (notmuch->user_header == NULL) if (notmuch->user_header == NULL)
return NOTMUCH_STATUS_OUT_OF_MEMORY; return NOTMUCH_STATUS_OUT_OF_MEMORY;
status = notmuch_database_get_config_list (notmuch, CONFIG_HEADER_PREFIX, &list); list = _notmuch_string_map_iterator_create (notmuch->config, CONFIG_HEADER_PREFIX, FALSE);
if (status) if (! list)
return status; INTERNAL_ERROR ("unable to read headers from configuration");
for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) { for (; _notmuch_string_map_iterator_valid (list);
_notmuch_string_map_iterator_move_to_next (list)) {
prefix_t query_field; prefix_t query_field;
const char *key = notmuch_config_list_key (list) const char *key = _notmuch_string_map_iterator_key (list)
+ sizeof (CONFIG_HEADER_PREFIX) - 1; + sizeof (CONFIG_HEADER_PREFIX) - 1;
_notmuch_string_map_append (notmuch->user_prefix, _notmuch_string_map_append (notmuch->user_prefix,
@ -194,7 +194,7 @@ _notmuch_database_setup_user_query_fields (notmuch_database_t *notmuch)
_notmuch_string_map_append (notmuch->user_header, _notmuch_string_map_append (notmuch->user_header,
key, key,
notmuch_config_list_value (list)); _notmuch_string_map_iterator_value (list));
query_field.name = talloc_strdup (notmuch, key); query_field.name = talloc_strdup (notmuch, key);
query_field.prefix = _user_prefix (notmuch, key); query_field.prefix = _user_prefix (notmuch, key);
@ -204,7 +204,7 @@ _notmuch_database_setup_user_query_fields (notmuch_database_t *notmuch)
_setup_query_field_default (&query_field, notmuch); _setup_query_field_default (&query_field, notmuch);
} }
notmuch_config_list_destroy (list); _notmuch_string_map_iterator_destroy (list);
return NOTMUCH_STATUS_SUCCESS; return NOTMUCH_STATUS_SUCCESS;
} }

View file

@ -340,7 +340,7 @@ test_expect_equal_file EXPECTED OUTPUT
test_begin_subtest "get config from closed database" test_begin_subtest "get config from closed database"
cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
{ {
const char *result; char *result;
EXPECT0(notmuch_database_close (db)); EXPECT0(notmuch_database_close (db));
stat = notmuch_database_get_config (db, "foo", &result); stat = notmuch_database_get_config (db, "foo", &result);
printf("%d\n", stat == NOTMUCH_STATUS_XAPIAN_EXCEPTION); printf("%d\n", stat == NOTMUCH_STATUS_XAPIAN_EXCEPTION);
@ -348,9 +348,8 @@ cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
EOF EOF
cat <<EOF > EXPECTED cat <<EOF > EXPECTED
== stdout == == stdout ==
1 0
== stderr == == stderr ==
Error: A Xapian exception occurred getting metadata: Database has been closed
EOF EOF
test_expect_equal_file EXPECTED OUTPUT test_expect_equal_file EXPECTED OUTPUT
@ -376,7 +375,7 @@ cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
notmuch_indexopts_t *result; notmuch_indexopts_t *result;
EXPECT0(notmuch_database_close (db)); EXPECT0(notmuch_database_close (db));
result = notmuch_database_get_default_indexopts (db); result = notmuch_database_get_default_indexopts (db);
printf("%d\n", result == NULL); printf("%d\n", result != NULL);
} }
EOF EOF
cat <<EOF > EXPECTED cat <<EOF > EXPECTED