* v1 _notmuch_message_ensure_metadata exception handling
@ 2017-02-25 3:45 David Bremner
2017-02-25 3:45 ` [PATCH 1/4] test: add known broken test for uncaught DatabaseModifiedError David Bremner
` (3 more replies)
0 siblings, 4 replies; 13+ messages in thread
From: David Bremner @ 2017-02-25 3:45 UTC (permalink / raw)
To: notmuch, eg
In id:1487583702.5ghl7kdkaw.astroid@strange.none Gaute provided a test
case that demonstrated an uncaught Xapian::DatabaseModifiedError in
_notmuch_message_ensure_metadata.
Starting with id:20170218144551.22925-2-david@tethera.net, I provided
one approach to dealing with this via error propagation. I believe the
current series is a better approach because it actually fixes the
problem (i.e. re-opens the database), and because it takes less code.
There are probably plenty of other places where the new
notmuch_database_open needs to be called.
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 1/4] test: add known broken test for uncaught DatabaseModifiedError
2017-02-25 3:45 v1 _notmuch_message_ensure_metadata exception handling David Bremner
@ 2017-02-25 3:45 ` David Bremner
2017-02-25 18:49 ` Jani Nikula
2017-02-25 3:45 ` [PATCH 2/4] lib: add notmuch_database_reopen David Bremner
` (2 subsequent siblings)
3 siblings, 1 reply; 13+ messages in thread
From: David Bremner @ 2017-02-25 3:45 UTC (permalink / raw)
To: notmuch, eg
There are several of these to track down, but one that is in quite a
few code paths is _notmuch_message_ensure_metadata.
---
test/T640-database-modified.sh | 60 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+)
create mode 100755 test/T640-database-modified.sh
diff --git a/test/T640-database-modified.sh b/test/T640-database-modified.sh
new file mode 100755
index 00000000..41869eaa
--- /dev/null
+++ b/test/T640-database-modified.sh
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+test_description="DatabaseModifiedError handling"
+. ./test-lib.sh || exit 1
+
+add_email_corpus
+
+test_begin_subtest "catching DatabaseModifiedError in _notmuch_message_ensure_metadata"
+test_subtest_known_broken
+test_C ${MAIL_DIR} <<'EOF'
+#include <unistd.h>
+#include <stdlib.h>
+#include <notmuch-test.h>
+#include <talloc.h>
+int
+main (int argc, char **argv)
+{
+ pid_t child;
+ const char *path = argv[1];
+
+ notmuch_database_t *rw_db, *ro_db;
+ notmuch_messages_t *messages;
+ notmuch_message_t *message, *ro_message;
+ notmuch_query_t *query;
+ notmuch_tags_t *tags;
+
+ EXPECT0 (notmuch_database_open (path, NOTMUCH_DATABASE_MODE_READ_ONLY, &ro_db));
+ EXPECT0 (notmuch_database_find_message (ro_db, "4EFC743A.3060609@april.org", &ro_message));
+
+ EXPECT0 (notmuch_database_open (path, NOTMUCH_DATABASE_MODE_READ_WRITE, &rw_db));
+ query = notmuch_query_create(rw_db, "");
+ EXPECT0 (notmuch_query_search_messages_st (query, &messages));
+
+ for (int count=0;
+ notmuch_messages_valid (messages);
+ notmuch_messages_move_to_next (messages), count++) {
+ message = notmuch_messages_get (messages);
+ for (int i=0; i<200; i++) {
+ char *tag_str = talloc_asprintf(rw_db, "%d", i);
+ EXPECT0 (notmuch_message_add_tag (message, tag_str));
+ talloc_free (tag_str);
+ }
+ }
+
+ notmuch_database_close (rw_db);
+
+ tags = notmuch_message_get_tags (ro_message);
+ if (tags)
+ printf("SUCCESS\n");
+ return 0;
+}
+EOF
+
+cat <<'EOF' >EXPECTED
+== stdout ==
+SUCCESS
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_done
--
2.11.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 2/4] lib: add notmuch_database_reopen
2017-02-25 3:45 v1 _notmuch_message_ensure_metadata exception handling David Bremner
2017-02-25 3:45 ` [PATCH 1/4] test: add known broken test for uncaught DatabaseModifiedError David Bremner
@ 2017-02-25 3:45 ` David Bremner
2017-02-25 18:33 ` Jani Nikula
2017-02-25 3:45 ` [PATCH 3/4] lib: handle DatabaseModifiedError in _n_message_ensure_metadata David Bremner
2017-02-25 3:45 ` [PATCH 4/4] lib/message.cc: use view number to invalidate cached metadata David Bremner
3 siblings, 1 reply; 13+ messages in thread
From: David Bremner @ 2017-02-25 3:45 UTC (permalink / raw)
To: notmuch, eg
The main expected use is to recover from a Xapian::DatabaseChanged
exception.
---
lib/database-private.h | 4 ++++
lib/database.cc | 23 +++++++++++++++++++++++
lib/notmuch.h | 12 ++++++++++++
3 files changed, 39 insertions(+)
diff --git a/lib/database-private.h b/lib/database-private.h
index 2fb60f5e..06882439 100644
--- a/lib/database-private.h
+++ b/lib/database-private.h
@@ -207,6 +207,10 @@ struct _notmuch_database {
unsigned long revision;
const char *uuid;
+ /* Keep track of the number of times the database has been re-opened
+ * (or other global invalidations of notmuch's caching)
+ */
+ unsigned long view;
Xapian::QueryParser *query_parser;
Xapian::TermGenerator *term_gen;
Xapian::ValueRangeProcessor *value_range_processor;
diff --git a/lib/database.cc b/lib/database.cc
index 386dcd17..1e958b65 100644
--- a/lib/database.cc
+++ b/lib/database.cc
@@ -951,6 +951,7 @@ notmuch_database_open_verbose (const char *path,
notmuch->mode = mode;
notmuch->atomic_nesting = 0;
+ notmuch->view = 1;
try {
string last_thread_id;
string last_mod;
@@ -1133,6 +1134,28 @@ notmuch_database_close (notmuch_database_t *notmuch)
return status;
}
+notmuch_status_t
+notmuch_database_reopen (notmuch_database_t *notmuch)
+{
+ if (notmuch->mode != NOTMUCH_DATABASE_MODE_READ_ONLY)
+ return NOTMUCH_STATUS_UNSUPPORTED_OPERATION;
+
+ try {
+ notmuch->xapian_db->reopen ();
+ } catch (const Xapian::Error &error) {
+ if (! notmuch->exception_reported) {
+ _notmuch_database_log (notmuch, "Error: A Xapian exception reopening database: %s\n",
+ error.get_msg ().c_str ());
+ notmuch->exception_reported = TRUE;
+ return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
+ }
+ }
+
+ notmuch->view++;
+
+ return NOTMUCH_STATUS_SUCCESS;
+}
+
static int
unlink_cb (const char *path,
unused (const struct stat *sb),
diff --git a/lib/notmuch.h b/lib/notmuch.h
index 16da8be9..e9ed01dd 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -363,6 +363,18 @@ notmuch_status_t
notmuch_database_close (notmuch_database_t *database);
/**
+ * Reopen a (read-only) database, synching the database view with that
+ * on disk.
+ *
+ * @returns
+ * - NOTMUCH_STATUS_UNSUPPORTED_OPERATION: database is not read only
+ * - NOTMUCH_STATUS_XAPIAN_EXCEPTION: a Xapian exception occured trying to
+ * re-open the database.
+ */
+notmuch_status_t
+notmuch_database_reopen (notmuch_database_t *database);
+
+/**
* A callback invoked by notmuch_database_compact to notify the user
* of the progress of the compaction process.
*/
--
2.11.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 3/4] lib: handle DatabaseModifiedError in _n_message_ensure_metadata
2017-02-25 3:45 v1 _notmuch_message_ensure_metadata exception handling David Bremner
2017-02-25 3:45 ` [PATCH 1/4] test: add known broken test for uncaught DatabaseModifiedError David Bremner
2017-02-25 3:45 ` [PATCH 2/4] lib: add notmuch_database_reopen David Bremner
@ 2017-02-25 3:45 ` David Bremner
2017-02-25 18:42 ` Jani Nikula
2017-02-25 3:45 ` [PATCH 4/4] lib/message.cc: use view number to invalidate cached metadata David Bremner
3 siblings, 1 reply; 13+ messages in thread
From: David Bremner @ 2017-02-25 3:45 UTC (permalink / raw)
To: notmuch, eg
The retries are hardcoded to a small number, and error handling aborts
than propagating errors from notmuch_database_reopen. These are both
somewhat justified by the assumption that most things that can go
wrong in Xapian::Database::reopen are rare and fatal (like running out
of memory or disk corruption).
---
lib/message.cc | 152 ++++++++++++++++++++++++-----------------
test/T640-database-modified.sh | 1 -
2 files changed, 88 insertions(+), 65 deletions(-)
diff --git a/lib/message.cc b/lib/message.cc
index 2fb67d85..15e2f528 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -49,6 +49,9 @@ struct visible _notmuch_message {
/* Message document modified since last sync */
notmuch_bool_t modified;
+ /* last view of database the struct is synced with */
+ unsigned long last_view;
+
Xapian::Document doc;
Xapian::termcount termpos;
};
@@ -110,6 +113,9 @@ _notmuch_message_create_for_document (const void *talloc_owner,
message->flags = 0;
message->lazy_flags = 0;
+ /* the message is initially not synchronized with Xapian */
+ message->last_view = 0;
+
/* Each of these will be lazily created as needed. */
message->message_id = NULL;
message->thread_id = NULL;
@@ -314,6 +320,8 @@ static void
_notmuch_message_ensure_metadata (notmuch_message_t *message)
{
Xapian::TermIterator i, end;
+ notmuch_bool_t success = FALSE;
+
const char *thread_prefix = _find_prefix ("thread"),
*tag_prefix = _find_prefix ("tag"),
*id_prefix = _find_prefix ("id"),
@@ -327,73 +335,89 @@ _notmuch_message_ensure_metadata (notmuch_message_t *message)
* slightly more costly than looking up individual fields if only
* one field of the message object is actually used, it's a huge
* win as more fields are used. */
+ for (int count=0; count < 3 && !success; count++) {
+ try {
+ i = message->doc.termlist_begin ();
+ end = message->doc.termlist_end ();
+
+ /* Get thread */
+ if (!message->thread_id)
+ message->thread_id =
+ _notmuch_message_get_term (message, i, end, thread_prefix);
+
+ /* Get tags */
+ assert (strcmp (thread_prefix, tag_prefix) < 0);
+ if (!message->tag_list) {
+ message->tag_list =
+ _notmuch_database_get_terms_with_prefix (message, i, end,
+ tag_prefix);
+ _notmuch_string_list_sort (message->tag_list);
+ }
- i = message->doc.termlist_begin ();
- end = message->doc.termlist_end ();
-
- /* Get thread */
- if (!message->thread_id)
- message->thread_id =
- _notmuch_message_get_term (message, i, end, thread_prefix);
-
- /* Get tags */
- assert (strcmp (thread_prefix, tag_prefix) < 0);
- if (!message->tag_list) {
- message->tag_list =
- _notmuch_database_get_terms_with_prefix (message, i, end,
- tag_prefix);
- _notmuch_string_list_sort (message->tag_list);
- }
+ /* Get id */
+ assert (strcmp (tag_prefix, id_prefix) < 0);
+ if (!message->message_id)
+ message->message_id =
+ _notmuch_message_get_term (message, i, end, id_prefix);
+
+ /* Get document type */
+ assert (strcmp (id_prefix, type_prefix) < 0);
+ if (! NOTMUCH_TEST_BIT (message->lazy_flags, NOTMUCH_MESSAGE_FLAG_GHOST)) {
+ i.skip_to (type_prefix);
+ /* "T" is the prefix "type" fields. See
+ * BOOLEAN_PREFIX_INTERNAL. */
+ if (*i == "Tmail")
+ NOTMUCH_CLEAR_BIT (&message->flags, NOTMUCH_MESSAGE_FLAG_GHOST);
+ else if (*i == "Tghost")
+ NOTMUCH_SET_BIT (&message->flags, NOTMUCH_MESSAGE_FLAG_GHOST);
+ else
+ INTERNAL_ERROR ("Message without type term");
+ NOTMUCH_SET_BIT (&message->lazy_flags, NOTMUCH_MESSAGE_FLAG_GHOST);
+ }
- /* Get id */
- assert (strcmp (tag_prefix, id_prefix) < 0);
- if (!message->message_id)
- message->message_id =
- _notmuch_message_get_term (message, i, end, id_prefix);
-
- /* Get document type */
- assert (strcmp (id_prefix, type_prefix) < 0);
- if (! NOTMUCH_TEST_BIT (message->lazy_flags, NOTMUCH_MESSAGE_FLAG_GHOST)) {
- i.skip_to (type_prefix);
- /* "T" is the prefix "type" fields. See
- * BOOLEAN_PREFIX_INTERNAL. */
- if (*i == "Tmail")
- NOTMUCH_CLEAR_BIT (&message->flags, NOTMUCH_MESSAGE_FLAG_GHOST);
- else if (*i == "Tghost")
- NOTMUCH_SET_BIT (&message->flags, NOTMUCH_MESSAGE_FLAG_GHOST);
- else
- INTERNAL_ERROR ("Message without type term");
- NOTMUCH_SET_BIT (&message->lazy_flags, NOTMUCH_MESSAGE_FLAG_GHOST);
+ /* Get filename list. Here we get only the terms. We lazily
+ * expand them to full file names when needed in
+ * _notmuch_message_ensure_filename_list. */
+ assert (strcmp (type_prefix, filename_prefix) < 0);
+ if (!message->filename_term_list && !message->filename_list)
+ message->filename_term_list =
+ _notmuch_database_get_terms_with_prefix (message, i, end,
+ filename_prefix);
+
+
+ /* Get property terms. Mimic the setup with filenames above */
+ assert (strcmp (filename_prefix, property_prefix) < 0);
+ if (!message->property_map && !message->property_term_list)
+ message->property_term_list =
+ _notmuch_database_get_terms_with_prefix (message, i, end,
+ property_prefix);
+
+ /* Get reply to */
+ assert (strcmp (property_prefix, replyto_prefix) < 0);
+ if (!message->in_reply_to)
+ message->in_reply_to =
+ _notmuch_message_get_term (message, i, end, replyto_prefix);
+
+
+ /* It's perfectly valid for a message to have no In-Reply-To
+ * header. For these cases, we return an empty string. */
+ if (!message->in_reply_to)
+ message->in_reply_to = talloc_strdup (message, "");
+
+ /* all the way without an exception */
+ success = TRUE;
+ } catch (const Xapian::DatabaseModifiedError &error) {
+ notmuch_status_t status = notmuch_database_reopen (message->notmuch);
+ if (status != NOTMUCH_STATUS_SUCCESS)
+ INTERNAL_ERROR ("unhandled error from notmuch_database_reopen: %s\n",
+ notmuch_status_to_string (status));
+ success = FALSE;
+ } catch (const Xapian::Error &error) {
+ INTERNAL_ERROR ("A Xapian exception occurred fetching message metadata: %s\n",
+ error.get_msg().c_str());
+ }
}
-
- /* Get filename list. Here we get only the terms. We lazily
- * expand them to full file names when needed in
- * _notmuch_message_ensure_filename_list. */
- assert (strcmp (type_prefix, filename_prefix) < 0);
- if (!message->filename_term_list && !message->filename_list)
- message->filename_term_list =
- _notmuch_database_get_terms_with_prefix (message, i, end,
- filename_prefix);
-
-
- /* Get property terms. Mimic the setup with filenames above */
- assert (strcmp (filename_prefix, property_prefix) < 0);
- if (!message->property_map && !message->property_term_list)
- message->property_term_list =
- _notmuch_database_get_terms_with_prefix (message, i, end,
- property_prefix);
-
- /* Get reply to */
- assert (strcmp (property_prefix, replyto_prefix) < 0);
- if (!message->in_reply_to)
- message->in_reply_to =
- _notmuch_message_get_term (message, i, end, replyto_prefix);
-
-
- /* It's perfectly valid for a message to have no In-Reply-To
- * header. For these cases, we return an empty string. */
- if (!message->in_reply_to)
- message->in_reply_to = talloc_strdup (message, "");
+ message->last_view = message->notmuch->view;
}
void
diff --git a/test/T640-database-modified.sh b/test/T640-database-modified.sh
index 41869eaa..9599417c 100755
--- a/test/T640-database-modified.sh
+++ b/test/T640-database-modified.sh
@@ -5,7 +5,6 @@ test_description="DatabaseModifiedError handling"
add_email_corpus
test_begin_subtest "catching DatabaseModifiedError in _notmuch_message_ensure_metadata"
-test_subtest_known_broken
test_C ${MAIL_DIR} <<'EOF'
#include <unistd.h>
#include <stdlib.h>
--
2.11.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 4/4] lib/message.cc: use view number to invalidate cached metadata
2017-02-25 3:45 v1 _notmuch_message_ensure_metadata exception handling David Bremner
` (2 preceding siblings ...)
2017-02-25 3:45 ` [PATCH 3/4] lib: handle DatabaseModifiedError in _n_message_ensure_metadata David Bremner
@ 2017-02-25 3:45 ` David Bremner
2017-02-25 18:44 ` Jani Nikula
3 siblings, 1 reply; 13+ messages in thread
From: David Bremner @ 2017-02-25 3:45 UTC (permalink / raw)
To: notmuch, eg
Currently the view number is incremented by notmuch_database_reopen
---
lib/message.cc | 25 +++++++++++--------------
1 file changed, 11 insertions(+), 14 deletions(-)
diff --git a/lib/message.cc b/lib/message.cc
index 15e2f528..bfb95917 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -317,11 +317,14 @@ _notmuch_message_get_term (notmuch_message_t *message,
}
static void
-_notmuch_message_ensure_metadata (notmuch_message_t *message)
+_notmuch_message_ensure_metadata (notmuch_message_t *message, void *field)
{
Xapian::TermIterator i, end;
notmuch_bool_t success = FALSE;
+ if ((field != NULL) && (message->last_view >= message->notmuch->view))
+ return;
+
const char *thread_prefix = _find_prefix ("thread"),
*tag_prefix = _find_prefix ("tag"),
*id_prefix = _find_prefix ("id"),
@@ -472,8 +475,7 @@ _notmuch_message_get_doc_id (notmuch_message_t *message)
const char *
notmuch_message_get_message_id (notmuch_message_t *message)
{
- if (!message->message_id)
- _notmuch_message_ensure_metadata (message);
+ _notmuch_message_ensure_metadata (message, message->message_id);
if (!message->message_id)
INTERNAL_ERROR ("Message with document ID of %u has no message ID.\n",
message->doc_id);
@@ -548,16 +550,14 @@ notmuch_message_get_header (notmuch_message_t *message, const char *header)
const char *
_notmuch_message_get_in_reply_to (notmuch_message_t *message)
{
- if (!message->in_reply_to)
- _notmuch_message_ensure_metadata (message);
+ _notmuch_message_ensure_metadata (message, message->in_reply_to);
return message->in_reply_to;
}
const char *
notmuch_message_get_thread_id (notmuch_message_t *message)
{
- if (!message->thread_id)
- _notmuch_message_ensure_metadata (message);
+ _notmuch_message_ensure_metadata (message, message->thread_id);
if (!message->thread_id)
INTERNAL_ERROR ("Message with document ID of %u has no thread ID.\n",
message->doc_id);
@@ -860,8 +860,7 @@ _notmuch_message_ensure_filename_list (notmuch_message_t *message)
if (message->filename_list)
return;
- if (!message->filename_term_list)
- _notmuch_message_ensure_metadata (message);
+ _notmuch_message_ensure_metadata (message, message->filename_term_list);
message->filename_list = _notmuch_string_list_create (message);
node = message->filename_term_list->head;
@@ -955,7 +954,7 @@ notmuch_message_get_flag (notmuch_message_t *message,
{
if (flag == NOTMUCH_MESSAGE_FLAG_GHOST &&
! NOTMUCH_TEST_BIT (message->lazy_flags, flag))
- _notmuch_message_ensure_metadata (message);
+ _notmuch_message_ensure_metadata (message, NULL);
return NOTMUCH_TEST_BIT (message->flags, flag);
}
@@ -996,8 +995,7 @@ notmuch_message_get_tags (notmuch_message_t *message)
{
notmuch_tags_t *tags;
- if (!message->tag_list)
- _notmuch_message_ensure_metadata (message);
+ _notmuch_message_ensure_metadata (message, message->tag_list);
tags = _notmuch_tags_create (message, message->tag_list);
/* _notmuch_tags_create steals the reference to the tag_list, but
@@ -1833,8 +1831,7 @@ _notmuch_message_ensure_property_map (notmuch_message_t *message)
if (message->property_map)
return;
- if (!message->property_term_list)
- _notmuch_message_ensure_metadata (message);
+ _notmuch_message_ensure_metadata (message, message->property_term_list);
message->property_map = _notmuch_string_map_create (message);
--
2.11.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 2/4] lib: add notmuch_database_reopen
2017-02-25 3:45 ` [PATCH 2/4] lib: add notmuch_database_reopen David Bremner
@ 2017-02-25 18:33 ` Jani Nikula
2017-02-25 20:11 ` David Bremner
0 siblings, 1 reply; 13+ messages in thread
From: Jani Nikula @ 2017-02-25 18:33 UTC (permalink / raw)
To: David Bremner, notmuch, eg
On Fri, 24 Feb 2017, David Bremner <david@tethera.net> wrote:
> The main expected use is to recover from a Xapian::DatabaseChanged
> exception.
I guess the main question here is if this should be exposed from the
library or not. If the intention is to recover *within* the library, why
add new API?
> ---
> lib/database-private.h | 4 ++++
> lib/database.cc | 23 +++++++++++++++++++++++
> lib/notmuch.h | 12 ++++++++++++
> 3 files changed, 39 insertions(+)
>
> diff --git a/lib/database-private.h b/lib/database-private.h
> index 2fb60f5e..06882439 100644
> --- a/lib/database-private.h
> +++ b/lib/database-private.h
> @@ -207,6 +207,10 @@ struct _notmuch_database {
> unsigned long revision;
> const char *uuid;
>
> + /* Keep track of the number of times the database has been re-opened
> + * (or other global invalidations of notmuch's caching)
> + */
> + unsigned long view;
> Xapian::QueryParser *query_parser;
> Xapian::TermGenerator *term_gen;
> Xapian::ValueRangeProcessor *value_range_processor;
> diff --git a/lib/database.cc b/lib/database.cc
> index 386dcd17..1e958b65 100644
> --- a/lib/database.cc
> +++ b/lib/database.cc
> @@ -951,6 +951,7 @@ notmuch_database_open_verbose (const char *path,
>
> notmuch->mode = mode;
> notmuch->atomic_nesting = 0;
> + notmuch->view = 1;
> try {
> string last_thread_id;
> string last_mod;
> @@ -1133,6 +1134,28 @@ notmuch_database_close (notmuch_database_t *notmuch)
> return status;
> }
>
> +notmuch_status_t
> +notmuch_database_reopen (notmuch_database_t *notmuch)
> +{
> + if (notmuch->mode != NOTMUCH_DATABASE_MODE_READ_ONLY)
> + return NOTMUCH_STATUS_UNSUPPORTED_OPERATION;
> +
> + try {
> + notmuch->xapian_db->reopen ();
> + } catch (const Xapian::Error &error) {
> + if (! notmuch->exception_reported) {
> + _notmuch_database_log (notmuch, "Error: A Xapian exception reopening database: %s\n",
> + error.get_msg ().c_str ());
> + notmuch->exception_reported = TRUE;
> + return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
> + }
Move the above return out of the if block?
> + }
> +
> + notmuch->view++;
> +
> + return NOTMUCH_STATUS_SUCCESS;
> +}
> +
> static int
> unlink_cb (const char *path,
> unused (const struct stat *sb),
> diff --git a/lib/notmuch.h b/lib/notmuch.h
> index 16da8be9..e9ed01dd 100644
> --- a/lib/notmuch.h
> +++ b/lib/notmuch.h
> @@ -363,6 +363,18 @@ notmuch_status_t
> notmuch_database_close (notmuch_database_t *database);
>
> /**
> + * Reopen a (read-only) database, synching the database view with that
> + * on disk.
> + *
> + * @returns
> + * - NOTMUCH_STATUS_UNSUPPORTED_OPERATION: database is not read only
> + * - NOTMUCH_STATUS_XAPIAN_EXCEPTION: a Xapian exception occured trying to
> + * re-open the database.
> + */
> +notmuch_status_t
> +notmuch_database_reopen (notmuch_database_t *database);
> +
> +/**
> * A callback invoked by notmuch_database_compact to notify the user
> * of the progress of the compaction process.
> */
> --
> 2.11.0
>
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org
> https://notmuchmail.org/mailman/listinfo/notmuch
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 3/4] lib: handle DatabaseModifiedError in _n_message_ensure_metadata
2017-02-25 3:45 ` [PATCH 3/4] lib: handle DatabaseModifiedError in _n_message_ensure_metadata David Bremner
@ 2017-02-25 18:42 ` Jani Nikula
2017-02-25 20:21 ` David Bremner
0 siblings, 1 reply; 13+ messages in thread
From: Jani Nikula @ 2017-02-25 18:42 UTC (permalink / raw)
To: David Bremner, notmuch, eg
On Fri, 24 Feb 2017, David Bremner <david@tethera.net> wrote:
> The retries are hardcoded to a small number, and error handling aborts
> than propagating errors from notmuch_database_reopen. These are both
> somewhat justified by the assumption that most things that can go
> wrong in Xapian::Database::reopen are rare and fatal (like running out
> of memory or disk corruption).
I think the sanity of the implementation hinges on that assumption. It
makes sense if you're right, but I really have no idea either way...
> ---
> lib/message.cc | 152 ++++++++++++++++++++++++-----------------
> test/T640-database-modified.sh | 1 -
> 2 files changed, 88 insertions(+), 65 deletions(-)
>
> diff --git a/lib/message.cc b/lib/message.cc
> index 2fb67d85..15e2f528 100644
> --- a/lib/message.cc
> +++ b/lib/message.cc
> @@ -49,6 +49,9 @@ struct visible _notmuch_message {
> /* Message document modified since last sync */
> notmuch_bool_t modified;
>
> + /* last view of database the struct is synced with */
> + unsigned long last_view;
> +
> Xapian::Document doc;
> Xapian::termcount termpos;
> };
> @@ -110,6 +113,9 @@ _notmuch_message_create_for_document (const void *talloc_owner,
> message->flags = 0;
> message->lazy_flags = 0;
>
> + /* the message is initially not synchronized with Xapian */
> + message->last_view = 0;
> +
> /* Each of these will be lazily created as needed. */
> message->message_id = NULL;
> message->thread_id = NULL;
> @@ -314,6 +320,8 @@ static void
> _notmuch_message_ensure_metadata (notmuch_message_t *message)
> {
> Xapian::TermIterator i, end;
> + notmuch_bool_t success = FALSE;
> +
> const char *thread_prefix = _find_prefix ("thread"),
> *tag_prefix = _find_prefix ("tag"),
> *id_prefix = _find_prefix ("id"),
> @@ -327,73 +335,89 @@ _notmuch_message_ensure_metadata (notmuch_message_t *message)
> * slightly more costly than looking up individual fields if only
> * one field of the message object is actually used, it's a huge
> * win as more fields are used. */
> + for (int count=0; count < 3 && !success; count++) {
> + try {
> + i = message->doc.termlist_begin ();
> + end = message->doc.termlist_end ();
> +
> + /* Get thread */
> + if (!message->thread_id)
> + message->thread_id =
> + _notmuch_message_get_term (message, i, end, thread_prefix);
> +
> + /* Get tags */
> + assert (strcmp (thread_prefix, tag_prefix) < 0);
> + if (!message->tag_list) {
> + message->tag_list =
> + _notmuch_database_get_terms_with_prefix (message, i, end,
> + tag_prefix);
> + _notmuch_string_list_sort (message->tag_list);
> + }
>
> - i = message->doc.termlist_begin ();
> - end = message->doc.termlist_end ();
> -
> - /* Get thread */
> - if (!message->thread_id)
> - message->thread_id =
> - _notmuch_message_get_term (message, i, end, thread_prefix);
> -
> - /* Get tags */
> - assert (strcmp (thread_prefix, tag_prefix) < 0);
> - if (!message->tag_list) {
> - message->tag_list =
> - _notmuch_database_get_terms_with_prefix (message, i, end,
> - tag_prefix);
> - _notmuch_string_list_sort (message->tag_list);
> - }
> + /* Get id */
> + assert (strcmp (tag_prefix, id_prefix) < 0);
> + if (!message->message_id)
> + message->message_id =
> + _notmuch_message_get_term (message, i, end, id_prefix);
> +
> + /* Get document type */
> + assert (strcmp (id_prefix, type_prefix) < 0);
> + if (! NOTMUCH_TEST_BIT (message->lazy_flags, NOTMUCH_MESSAGE_FLAG_GHOST)) {
> + i.skip_to (type_prefix);
> + /* "T" is the prefix "type" fields. See
> + * BOOLEAN_PREFIX_INTERNAL. */
> + if (*i == "Tmail")
> + NOTMUCH_CLEAR_BIT (&message->flags, NOTMUCH_MESSAGE_FLAG_GHOST);
> + else if (*i == "Tghost")
> + NOTMUCH_SET_BIT (&message->flags, NOTMUCH_MESSAGE_FLAG_GHOST);
> + else
> + INTERNAL_ERROR ("Message without type term");
> + NOTMUCH_SET_BIT (&message->lazy_flags, NOTMUCH_MESSAGE_FLAG_GHOST);
> + }
>
> - /* Get id */
> - assert (strcmp (tag_prefix, id_prefix) < 0);
> - if (!message->message_id)
> - message->message_id =
> - _notmuch_message_get_term (message, i, end, id_prefix);
> -
> - /* Get document type */
> - assert (strcmp (id_prefix, type_prefix) < 0);
> - if (! NOTMUCH_TEST_BIT (message->lazy_flags, NOTMUCH_MESSAGE_FLAG_GHOST)) {
> - i.skip_to (type_prefix);
> - /* "T" is the prefix "type" fields. See
> - * BOOLEAN_PREFIX_INTERNAL. */
> - if (*i == "Tmail")
> - NOTMUCH_CLEAR_BIT (&message->flags, NOTMUCH_MESSAGE_FLAG_GHOST);
> - else if (*i == "Tghost")
> - NOTMUCH_SET_BIT (&message->flags, NOTMUCH_MESSAGE_FLAG_GHOST);
> - else
> - INTERNAL_ERROR ("Message without type term");
> - NOTMUCH_SET_BIT (&message->lazy_flags, NOTMUCH_MESSAGE_FLAG_GHOST);
> + /* Get filename list. Here we get only the terms. We lazily
> + * expand them to full file names when needed in
> + * _notmuch_message_ensure_filename_list. */
> + assert (strcmp (type_prefix, filename_prefix) < 0);
> + if (!message->filename_term_list && !message->filename_list)
> + message->filename_term_list =
> + _notmuch_database_get_terms_with_prefix (message, i, end,
> + filename_prefix);
> +
> +
> + /* Get property terms. Mimic the setup with filenames above */
> + assert (strcmp (filename_prefix, property_prefix) < 0);
> + if (!message->property_map && !message->property_term_list)
> + message->property_term_list =
> + _notmuch_database_get_terms_with_prefix (message, i, end,
> + property_prefix);
> +
> + /* Get reply to */
> + assert (strcmp (property_prefix, replyto_prefix) < 0);
> + if (!message->in_reply_to)
> + message->in_reply_to =
> + _notmuch_message_get_term (message, i, end, replyto_prefix);
> +
> +
> + /* It's perfectly valid for a message to have no In-Reply-To
> + * header. For these cases, we return an empty string. */
> + if (!message->in_reply_to)
> + message->in_reply_to = talloc_strdup (message, "");
> +
> + /* all the way without an exception */
> + success = TRUE;
Nitpick, if you don't intend to use that variable to return status from
the function, you can just break here, and get rid of the variable. But
no big deal.
> + } catch (const Xapian::DatabaseModifiedError &error) {
> + notmuch_status_t status = notmuch_database_reopen (message->notmuch);
> + if (status != NOTMUCH_STATUS_SUCCESS)
> + INTERNAL_ERROR ("unhandled error from notmuch_database_reopen: %s\n",
> + notmuch_status_to_string (status));
> + success = FALSE;
> + } catch (const Xapian::Error &error) {
> + INTERNAL_ERROR ("A Xapian exception occurred fetching message metadata: %s\n",
> + error.get_msg().c_str());
> + }
If the assumption is that these really are rare cases (read: shouldn't
happen), INTERNAL_ERROR is an improvement over leaking the
exception. Otherwise, I think we'd need to propagate the status all the
way to the API, which would really be annoying.
I guess I think this is a worthwhile improvement no matter what.
> }
> -
> - /* Get filename list. Here we get only the terms. We lazily
> - * expand them to full file names when needed in
> - * _notmuch_message_ensure_filename_list. */
> - assert (strcmp (type_prefix, filename_prefix) < 0);
> - if (!message->filename_term_list && !message->filename_list)
> - message->filename_term_list =
> - _notmuch_database_get_terms_with_prefix (message, i, end,
> - filename_prefix);
> -
> -
> - /* Get property terms. Mimic the setup with filenames above */
> - assert (strcmp (filename_prefix, property_prefix) < 0);
> - if (!message->property_map && !message->property_term_list)
> - message->property_term_list =
> - _notmuch_database_get_terms_with_prefix (message, i, end,
> - property_prefix);
> -
> - /* Get reply to */
> - assert (strcmp (property_prefix, replyto_prefix) < 0);
> - if (!message->in_reply_to)
> - message->in_reply_to =
> - _notmuch_message_get_term (message, i, end, replyto_prefix);
> -
> -
> - /* It's perfectly valid for a message to have no In-Reply-To
> - * header. For these cases, we return an empty string. */
> - if (!message->in_reply_to)
> - message->in_reply_to = talloc_strdup (message, "");
> + message->last_view = message->notmuch->view;
> }
>
> void
> diff --git a/test/T640-database-modified.sh b/test/T640-database-modified.sh
> index 41869eaa..9599417c 100755
> --- a/test/T640-database-modified.sh
> +++ b/test/T640-database-modified.sh
> @@ -5,7 +5,6 @@ test_description="DatabaseModifiedError handling"
> add_email_corpus
>
> test_begin_subtest "catching DatabaseModifiedError in _notmuch_message_ensure_metadata"
> -test_subtest_known_broken
> test_C ${MAIL_DIR} <<'EOF'
> #include <unistd.h>
> #include <stdlib.h>
> --
> 2.11.0
>
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org
> https://notmuchmail.org/mailman/listinfo/notmuch
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 4/4] lib/message.cc: use view number to invalidate cached metadata
2017-02-25 3:45 ` [PATCH 4/4] lib/message.cc: use view number to invalidate cached metadata David Bremner
@ 2017-02-25 18:44 ` Jani Nikula
0 siblings, 0 replies; 13+ messages in thread
From: Jani Nikula @ 2017-02-25 18:44 UTC (permalink / raw)
To: David Bremner, notmuch, eg
On Fri, 24 Feb 2017, David Bremner <david@tethera.net> wrote:
> Currently the view number is incremented by notmuch_database_reopen
> ---
> lib/message.cc | 25 +++++++++++--------------
> 1 file changed, 11 insertions(+), 14 deletions(-)
>
> diff --git a/lib/message.cc b/lib/message.cc
> index 15e2f528..bfb95917 100644
> --- a/lib/message.cc
> +++ b/lib/message.cc
> @@ -317,11 +317,14 @@ _notmuch_message_get_term (notmuch_message_t *message,
> }
>
> static void
> -_notmuch_message_ensure_metadata (notmuch_message_t *message)
> +_notmuch_message_ensure_metadata (notmuch_message_t *message, void *field)
> {
> Xapian::TermIterator i, end;
> notmuch_bool_t success = FALSE;
>
> + if ((field != NULL) && (message->last_view >= message->notmuch->view))
Nitpick, excessive braces for my taste!
> + return;
> +
> const char *thread_prefix = _find_prefix ("thread"),
> *tag_prefix = _find_prefix ("tag"),
> *id_prefix = _find_prefix ("id"),
> @@ -472,8 +475,7 @@ _notmuch_message_get_doc_id (notmuch_message_t *message)
> const char *
> notmuch_message_get_message_id (notmuch_message_t *message)
> {
> - if (!message->message_id)
> - _notmuch_message_ensure_metadata (message);
> + _notmuch_message_ensure_metadata (message, message->message_id);
> if (!message->message_id)
Side note, looks like we don't consistently check the metadata we want
is really available.
All in all, LGTM.
> INTERNAL_ERROR ("Message with document ID of %u has no message ID.\n",
> message->doc_id);
> @@ -548,16 +550,14 @@ notmuch_message_get_header (notmuch_message_t *message, const char *header)
> const char *
> _notmuch_message_get_in_reply_to (notmuch_message_t *message)
> {
> - if (!message->in_reply_to)
> - _notmuch_message_ensure_metadata (message);
> + _notmuch_message_ensure_metadata (message, message->in_reply_to);
> return message->in_reply_to;
> }
>
> const char *
> notmuch_message_get_thread_id (notmuch_message_t *message)
> {
> - if (!message->thread_id)
> - _notmuch_message_ensure_metadata (message);
> + _notmuch_message_ensure_metadata (message, message->thread_id);
> if (!message->thread_id)
> INTERNAL_ERROR ("Message with document ID of %u has no thread ID.\n",
> message->doc_id);
> @@ -860,8 +860,7 @@ _notmuch_message_ensure_filename_list (notmuch_message_t *message)
> if (message->filename_list)
> return;
>
> - if (!message->filename_term_list)
> - _notmuch_message_ensure_metadata (message);
> + _notmuch_message_ensure_metadata (message, message->filename_term_list);
>
> message->filename_list = _notmuch_string_list_create (message);
> node = message->filename_term_list->head;
> @@ -955,7 +954,7 @@ notmuch_message_get_flag (notmuch_message_t *message,
> {
> if (flag == NOTMUCH_MESSAGE_FLAG_GHOST &&
> ! NOTMUCH_TEST_BIT (message->lazy_flags, flag))
> - _notmuch_message_ensure_metadata (message);
> + _notmuch_message_ensure_metadata (message, NULL);
>
> return NOTMUCH_TEST_BIT (message->flags, flag);
> }
> @@ -996,8 +995,7 @@ notmuch_message_get_tags (notmuch_message_t *message)
> {
> notmuch_tags_t *tags;
>
> - if (!message->tag_list)
> - _notmuch_message_ensure_metadata (message);
> + _notmuch_message_ensure_metadata (message, message->tag_list);
>
> tags = _notmuch_tags_create (message, message->tag_list);
> /* _notmuch_tags_create steals the reference to the tag_list, but
> @@ -1833,8 +1831,7 @@ _notmuch_message_ensure_property_map (notmuch_message_t *message)
> if (message->property_map)
> return;
>
> - if (!message->property_term_list)
> - _notmuch_message_ensure_metadata (message);
> + _notmuch_message_ensure_metadata (message, message->property_term_list);
>
> message->property_map = _notmuch_string_map_create (message);
>
> --
> 2.11.0
>
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org
> https://notmuchmail.org/mailman/listinfo/notmuch
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/4] test: add known broken test for uncaught DatabaseModifiedError
2017-02-25 3:45 ` [PATCH 1/4] test: add known broken test for uncaught DatabaseModifiedError David Bremner
@ 2017-02-25 18:49 ` Jani Nikula
2017-02-25 20:08 ` David Bremner
0 siblings, 1 reply; 13+ messages in thread
From: Jani Nikula @ 2017-02-25 18:49 UTC (permalink / raw)
To: David Bremner, notmuch, eg
On Fri, 24 Feb 2017, David Bremner <david@tethera.net> wrote:
> There are several of these to track down, but one that is in quite a
> few code paths is _notmuch_message_ensure_metadata.
> ---
> test/T640-database-modified.sh | 60 ++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 60 insertions(+)
> create mode 100755 test/T640-database-modified.sh
>
> diff --git a/test/T640-database-modified.sh b/test/T640-database-modified.sh
> new file mode 100755
> index 00000000..41869eaa
> --- /dev/null
> +++ b/test/T640-database-modified.sh
> @@ -0,0 +1,60 @@
> +#!/usr/bin/env bash
> +test_description="DatabaseModifiedError handling"
> +. ./test-lib.sh || exit 1
> +
> +add_email_corpus
Is the whole corpus necessary? Just add a single message?
> +
> +test_begin_subtest "catching DatabaseModifiedError in _notmuch_message_ensure_metadata"
> +test_subtest_known_broken
> +test_C ${MAIL_DIR} <<'EOF'
I'm not entirely thrilled by the whole test_C thing, but I guess I've
lost that battle... :/
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <notmuch-test.h>
> +#include <talloc.h>
> +int
> +main (int argc, char **argv)
> +{
> + pid_t child;
Leftover variable?
> + const char *path = argv[1];
> +
> + notmuch_database_t *rw_db, *ro_db;
> + notmuch_messages_t *messages;
> + notmuch_message_t *message, *ro_message;
> + notmuch_query_t *query;
> + notmuch_tags_t *tags;
> +
> + EXPECT0 (notmuch_database_open (path, NOTMUCH_DATABASE_MODE_READ_ONLY, &ro_db));
> + EXPECT0 (notmuch_database_find_message (ro_db, "4EFC743A.3060609@april.org", &ro_message));
How about add_message with a specific message-id, and using that here? I
think we rely too much on the test corpus and hard-code stuff from there
too much.
Otherwise, LGTM.
> +
> + EXPECT0 (notmuch_database_open (path, NOTMUCH_DATABASE_MODE_READ_WRITE, &rw_db));
> + query = notmuch_query_create(rw_db, "");
> + EXPECT0 (notmuch_query_search_messages_st (query, &messages));
> +
> + for (int count=0;
> + notmuch_messages_valid (messages);
> + notmuch_messages_move_to_next (messages), count++) {
> + message = notmuch_messages_get (messages);
> + for (int i=0; i<200; i++) {
> + char *tag_str = talloc_asprintf(rw_db, "%d", i);
> + EXPECT0 (notmuch_message_add_tag (message, tag_str));
> + talloc_free (tag_str);
> + }
> + }
> +
> + notmuch_database_close (rw_db);
> +
> + tags = notmuch_message_get_tags (ro_message);
> + if (tags)
> + printf("SUCCESS\n");
> + return 0;
> +}
> +EOF
> +
> +cat <<'EOF' >EXPECTED
> +== stdout ==
> +SUCCESS
> +== stderr ==
> +EOF
> +test_expect_equal_file EXPECTED OUTPUT
> +
> +test_done
> --
> 2.11.0
>
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org
> https://notmuchmail.org/mailman/listinfo/notmuch
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/4] test: add known broken test for uncaught DatabaseModifiedError
2017-02-25 18:49 ` Jani Nikula
@ 2017-02-25 20:08 ` David Bremner
0 siblings, 0 replies; 13+ messages in thread
From: David Bremner @ 2017-02-25 20:08 UTC (permalink / raw)
To: Jani Nikula, notmuch, eg
Jani Nikula <jani@nikula.org> writes:
>> +
>> +add_email_corpus
>
> Is the whole corpus necessary? Just add a single message?
Good point, that's actually leftover from a previous failed attempt.
>> +test_begin_subtest "catching DatabaseModifiedError in _notmuch_message_ensure_metadata"
>> +test_subtest_known_broken
>> +test_C ${MAIL_DIR} <<'EOF'
>
> I'm not entirely thrilled by the whole test_C thing, but I guess I've
> lost that battle... :/
>
>> +#include <unistd.h>
>> +#include <stdlib.h>
>> +#include <notmuch-test.h>
>> +#include <talloc.h>
>> +int
>> +main (int argc, char **argv)
>> +{
>> + pid_t child;
>
> Leftover variable?
correct.
> How about add_message with a specific message-id, and using that here? I
> think we rely too much on the test corpus and hard-code stuff from there
> too much.
Good idea.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/4] lib: add notmuch_database_reopen
2017-02-25 18:33 ` Jani Nikula
@ 2017-02-25 20:11 ` David Bremner
0 siblings, 0 replies; 13+ messages in thread
From: David Bremner @ 2017-02-25 20:11 UTC (permalink / raw)
To: Jani Nikula, notmuch, eg
Jani Nikula <jani@nikula.org> writes:
> On Fri, 24 Feb 2017, David Bremner <david@tethera.net> wrote:
>> The main expected use is to recover from a Xapian::DatabaseChanged
>> exception.
>
> I guess the main question here is if this should be exposed from the
> library or not. If the intention is to recover *within* the library, why
> add new API?
Yes that would work. Although as I mentioned on IRC, the API has been
proposed a few times before. I don't mind either way, it could always be
exposed later. In particular then I could feel less guilty about not yet
providing any bindings.
>> + notmuch->xapian_db->reopen ();
>> + } catch (const Xapian::Error &error) {
>> + if (! notmuch->exception_reported) {
>> + _notmuch_database_log (notmuch, "Error: A Xapian exception reopening database: %s\n",
>> + error.get_msg ().c_str ());
>> + notmuch->exception_reported = TRUE;
>> + return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
>> + }
>
> Move the above return out of the if block?
oh duh. good catch.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 3/4] lib: handle DatabaseModifiedError in _n_message_ensure_metadata
2017-02-25 18:42 ` Jani Nikula
@ 2017-02-25 20:21 ` David Bremner
2017-02-25 20:39 ` Jani Nikula
0 siblings, 1 reply; 13+ messages in thread
From: David Bremner @ 2017-02-25 20:21 UTC (permalink / raw)
To: Jani Nikula, notmuch, eg
Jani Nikula <jani@nikula.org> writes:
> On Fri, 24 Feb 2017, David Bremner <david@tethera.net> wrote:
>> The retries are hardcoded to a small number, and error handling aborts
>> than propagating errors from notmuch_database_reopen. These are both
>> somewhat justified by the assumption that most things that can go
>> wrong in Xapian::Database::reopen are rare and fatal (like running out
>> of memory or disk corruption).
>
> I think the sanity of the implementation hinges on that assumption. It
> makes sense if you're right, but I really have no idea either way...
That was my conclusion from talking to Olly (xapian upstream).
24-02-2017 08:12:57 < bremner> any intuition about how likely
Xapian::Database::reopen is to fail? I'm catching a
DatabaseModifiedError somewhere where handling any further errors is
tricky, and wondering about treating a failed reopen as as "the
impossible happened, stopping"
24-02-2017 16:22:34 < olly> bremner: there should not be much scope for
failure - stuff like out of memory or disk errors, which are probably a
good enough excuse to stop
I could add that to the commit message?
>> +
>> + /* all the way without an exception */
>> + success = TRUE;
>
> Nitpick, if you don't intend to use that variable to return status from
> the function, you can just break here, and get rid of the variable. But
> no big deal.
>
I think I have some kind of mental block about break and continue. But
it could even be a goto, those I understand ;).
>
>> + } catch (const Xapian::DatabaseModifiedError &error) {
>> + notmuch_status_t status = notmuch_database_reopen (message->notmuch);
>> + if (status != NOTMUCH_STATUS_SUCCESS)
>> + INTERNAL_ERROR ("unhandled error from notmuch_database_reopen: %s\n",
>> + notmuch_status_to_string (status));
>> + success = FALSE;
>> + } catch (const Xapian::Error &error) {
>> + INTERNAL_ERROR ("A Xapian exception occurred fetching message metadata: %s\n",
>> + error.get_msg().c_str());
>> + }
>
> If the assumption is that these really are rare cases (read: shouldn't
> happen), INTERNAL_ERROR is an improvement over leaking the
> exception. Otherwise, I think we'd need to propagate the status all the
> way to the API, which would really be annoying.
>
> I guess I think this is a worthwhile improvement no matter what.
Yeah, I had a go at that in the previous longer series, but I was not
very happy with the (incomplete) results
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 3/4] lib: handle DatabaseModifiedError in _n_message_ensure_metadata
2017-02-25 20:21 ` David Bremner
@ 2017-02-25 20:39 ` Jani Nikula
0 siblings, 0 replies; 13+ messages in thread
From: Jani Nikula @ 2017-02-25 20:39 UTC (permalink / raw)
To: David Bremner, notmuch, eg
On Sat, 25 Feb 2017, David Bremner <david@tethera.net> wrote:
> Jani Nikula <jani@nikula.org> writes:
>
>> On Fri, 24 Feb 2017, David Bremner <david@tethera.net> wrote:
>>> The retries are hardcoded to a small number, and error handling aborts
>>> than propagating errors from notmuch_database_reopen. These are both
>>> somewhat justified by the assumption that most things that can go
>>> wrong in Xapian::Database::reopen are rare and fatal (like running out
>>> of memory or disk corruption).
>>
>> I think the sanity of the implementation hinges on that assumption. It
>> makes sense if you're right, but I really have no idea either way...
>
> That was my conclusion from talking to Olly (xapian upstream).
>
> 24-02-2017 08:12:57 < bremner> any intuition about how likely
> Xapian::Database::reopen is to fail? I'm catching a
> DatabaseModifiedError somewhere where handling any further errors is
> tricky, and wondering about treating a failed reopen as as "the
> impossible happened, stopping"
>
> 24-02-2017 16:22:34 < olly> bremner: there should not be much scope for
> failure - stuff like out of memory or disk errors, which are probably a
> good enough excuse to stop
>
> I could add that to the commit message?
Yes, please, it'll come in handy when the memory of the discussion has
faded! (Like two weeks from now... ;)
>
>>> +
>>> + /* all the way without an exception */
>>> + success = TRUE;
>>
>> Nitpick, if you don't intend to use that variable to return status from
>> the function, you can just break here, and get rid of the variable. But
>> no big deal.
>>
>
> I think I have some kind of mental block about break and continue. But
> it could even be a goto, those I understand ;).
Heh, as I said, no big deal.
>
>>
>>> + } catch (const Xapian::DatabaseModifiedError &error) {
>>> + notmuch_status_t status = notmuch_database_reopen (message->notmuch);
>>> + if (status != NOTMUCH_STATUS_SUCCESS)
>>> + INTERNAL_ERROR ("unhandled error from notmuch_database_reopen: %s\n",
>>> + notmuch_status_to_string (status));
>>> + success = FALSE;
>>> + } catch (const Xapian::Error &error) {
>>> + INTERNAL_ERROR ("A Xapian exception occurred fetching message metadata: %s\n",
>>> + error.get_msg().c_str());
>>> + }
>>
>> If the assumption is that these really are rare cases (read: shouldn't
>> happen), INTERNAL_ERROR is an improvement over leaking the
>> exception. Otherwise, I think we'd need to propagate the status all the
>> way to the API, which would really be annoying.
>>
>> I guess I think this is a worthwhile improvement no matter what.
>
> Yeah, I had a go at that in the previous longer series, but I was not
> very happy with the (incomplete) results
Err, I'm sorry for not being clear, I meant that this patch *series* is
a worthwhile improvement as-is, even if reopen did have common and
unrecoverable failure modes (which it shouldn't). This series catches a
previously uncaught exception, tries to do something sensible about it,
and failing at that, causes an INTERNAL_ERROR. That's a huge
improvement. If we end up seeing those errors later, we can reconsider
the error propagation.
BR,
Jani.
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2017-02-25 20:39 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-02-25 3:45 v1 _notmuch_message_ensure_metadata exception handling David Bremner
2017-02-25 3:45 ` [PATCH 1/4] test: add known broken test for uncaught DatabaseModifiedError David Bremner
2017-02-25 18:49 ` Jani Nikula
2017-02-25 20:08 ` David Bremner
2017-02-25 3:45 ` [PATCH 2/4] lib: add notmuch_database_reopen David Bremner
2017-02-25 18:33 ` Jani Nikula
2017-02-25 20:11 ` David Bremner
2017-02-25 3:45 ` [PATCH 3/4] lib: handle DatabaseModifiedError in _n_message_ensure_metadata David Bremner
2017-02-25 18:42 ` Jani Nikula
2017-02-25 20:21 ` David Bremner
2017-02-25 20:39 ` Jani Nikula
2017-02-25 3:45 ` [PATCH 4/4] lib/message.cc: use view number to invalidate cached metadata David Bremner
2017-02-25 18:44 ` Jani Nikula
Code repositories for project(s) associated with this public inbox
https://yhetil.org/notmuch.git/
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).