Sync index with retry to gracefully handle index locks
authorAdam Dullage <redacted>
Fri, 17 May 2024 07:30:25 +0000 (08:30 +0100)
committerAdam Dullage <redacted>
Fri, 17 May 2024 07:30:25 +0000 (08:30 +0100)
server/notes/file_system/file_system.py

index 9655e7bf0f1f2629833117dc23b192e177f9516d..532d636ce1d0744272210f9e446757415170c76b 100644 (file)
@@ -2,6 +2,7 @@ import glob
 import os\r
 import re\r
 import shutil\r
+import time\r
 from datetime import datetime\r
 from typing import List, Literal, Set, Tuple\r
 \r
@@ -10,7 +11,7 @@ from whoosh import writing
 from whoosh.analysis import CharsetFilter, StemmingAnalyzer\r
 from whoosh.fields import DATETIME, ID, KEYWORD, TEXT, SchemaClass\r
 from whoosh.highlight import ContextFragmenter, WholeFragmenter\r
-from whoosh.index import Index\r
+from whoosh.index import Index, LockError\r
 from whoosh.qparser import MultifieldParser\r
 from whoosh.qparser.dateparse import DateParserPlugin\r
 from whoosh.query import Every\r
@@ -51,7 +52,7 @@ class FileSystemNotes(BaseNotes):
                 f"'{self.storage_path}' is not a valid directory."\r
             )\r
         self.index = self._load_index()\r
-        self._sync_index(optimize=True)\r
+        self._sync_index_with_retry(optimize=True)\r
 \r
     def create(self, data: NoteCreate) -> Note:\r
         """Create a new note."""\r
@@ -108,7 +109,7 @@ class FileSystemNotes(BaseNotes):
         limit: int = None,\r
     ) -> Tuple[SearchResult, ...]:\r
         """Search the index for the given term."""\r
-        self._sync_index()\r
+        self._sync_index_with_retry()\r
         term = self._pre_process_search_term(term)\r
         with self.index.searcher() as searcher:\r
             # Parse Query\r
@@ -147,7 +148,7 @@ class FileSystemNotes(BaseNotes):
     def get_tags(self) -> list[str]:\r
         """Return a list of all indexed tags. Note: Tags no longer in use will\r
         only be cleared when the index is next optimized."""\r
-        self._sync_index()\r
+        self._sync_index_with_retry()\r
         with self.index.reader() as reader:\r
             tags = reader.field_terms("tags")\r
             return [tag for tag in tags]\r
@@ -260,6 +261,23 @@ class FileSystemNotes(BaseNotes):
                 )\r
                 logger.info(f"'{filename}' added to index")\r
         writer.commit(optimize=optimize)\r
+        logger.info("Index synchronized")\r
+\r
+    def _sync_index_with_retry(\r
+        self,\r
+        optimize: bool = False,\r
+        clean: bool = False,\r
+        max_retries: int = 8,\r
+        retry_delay: float = 0.25,\r
+    ) -> None:\r
+        for _ in range(max_retries):\r
+            try:\r
+                self._sync_index(optimize=optimize, clean=clean)\r
+                return\r
+            except LockError:\r
+                logger.warning(f"Index locked, retrying in {retry_delay}s")\r
+                time.sleep(retry_delay)\r
+        logger.error(f"Failed to sync index after {max_retries} retries")\r
 \r
     @classmethod\r
     def _pre_process_search_term(cls, term):\r
git clone https://git.99rst.org/PROJECT