This is an automated email from the ASF dual-hosted git repository.

sebb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-ponymail-foal.git


The following commit(s) were added to refs/heads/master by this push:
     new 3bd8a69  Permalinks etc can be dropped when importing mails
3bd8a69 is described below

commit 3bd8a69713440cb3acacd97233c22c34786e4c71
Author: Sebb <[email protected]>
AuthorDate: Mon Jan 17 00:08:08 2022 +0000

    Permalinks etc can be dropped when importing mails
    
    This relates to #193.
    Changes the default behaviour so that duplicates are rejected,
    rather than replacing an existing entry with possible data loss.
    
    Previous behaviour can be restored using the --overwrite flag
---
 tools/import-mbox.py | 71 +++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 56 insertions(+), 15 deletions(-)

diff --git a/tools/import-mbox.py b/tools/import-mbox.py
index 661ca5c..e568206 100755
--- a/tools/import-mbox.py
+++ b/tools/import-mbox.py
@@ -51,6 +51,8 @@ else:
 TIMEOUT_DEFAULT = 600
 goodies = 0
 baddies = 0
+dupes = 0 # number of duplicates dropped
+replacements = 0 # number of entries replaced
 duplicates: dict = {}  # detect if mid is re-used this run
 block = Lock()
 lists: list = []  # N.B. the entries in this list depend on the import type:
@@ -90,6 +92,7 @@ def bulk_insert(name, json, xes, dbindex, wc="quorum"):
 
     sys.stderr.flush()
 
+    optype =  "index" if args.overwrite else "create"
     js_arr = []
     for entry in json:
         js = entry
@@ -98,7 +101,7 @@ def bulk_insert(name, json, xes, dbindex, wc="quorum"):
             del js["mid"]
         js_arr.append(
             {
-                "_op_type": "index",
+                "_op_type": optype,
                 "_consistency": wc,
                 "_index": dbindex,
                 "_id": document_id,
@@ -106,11 +109,42 @@ def bulk_insert(name, json, xes, dbindex, wc="quorum"):
                 "_source": js,
             }
         )
+    successes = [] # indices of successful operations
+    findex = 0
+    repl = 0
+
+    # process the bulk responses individually so can determine
+    # which source entries need to be skipped
     try:
-        xes.bulk(js_arr)
-    #       print("%s: Inserted %u entries into %s" % (name, 
len(js_arr),dbindex))
-    except Exception as err:
+        for status, result in xes.streaming_bulk(js_arr, ignore_status=409):
+            d = result[optype]
+            if status:
+                successes.append(findex) # record successful entries
+                if d['result'] == 'updated':
+                    repl += 1
+            else:
+                msgid = js_arr[findex]['doc'].get('message-id', 'Unknown')
+                print(f"{name}: Warning: Failed to create {d['_index']} with 
mid: {d['_id']} from msgid: {msgid}")
+            findex += 1
+    except Exception as err: # should not happen
         print("%s: Warning: Could not bulk insert: %s into %s" % (name, err, 
dbindex))
+    return successes, repl
+
+def bulk_insert_both(name, mbox, source, xes):
+    """Create mbox entries; if any fail, don't create the corresponding source 
entries"""
+    global replacements, dupes, goodies
+    successes, repl = bulk_insert(name, mbox, xes, xes.db_mbox)
+    failures = len(mbox) - len(successes)
+    # if there are failures, keep only successes
+    if failures:
+        source = [source[i] for i in successes]
+    # anything left?
+    if source:
+        # not interested in replacements here
+        bulk_insert(name, source, xes, xes.db_source)
+    replacements += repl
+    goodies -= failures
+    dupes += failures
 
 class DownloadThread(Thread): # handles Pipermail
     def assign(self, url):
@@ -399,10 +433,8 @@ class SlurpThread(Thread):
                                 body={"source": contents[key]},
                             )
                     if len(ja) >= 40:
-                        bulk_insert(self.name, ja, es, es.db_mbox)
+                        bulk_insert_both(self.name, ja, jas, es)
                         ja = []
-
-                        bulk_insert(self.name, jas, es, es.db_source)
                         jas = []
                 else:
                     self.printid(
@@ -429,11 +461,8 @@ class SlurpThread(Thread):
             goodies += count
             baddies += bad
             if len(ja) > 0 and not args.dry:
-                bulk_insert(self.name, ja, es, es.db_mbox)
+                bulk_insert_both(self.name, ja, jas, es)
             ja = []
-
-            if len(jas) > 0 and not args.dry:
-                bulk_insert(self.name, jas, es, es.db_source)
             jas = []
         self.printid("Done, %u elements left to slurp" % len(lists))
 
@@ -565,6 +594,12 @@ parser.add_argument(
     help="Don't import a message if its Message-Id already exists on the list",
 )
 parser.add_argument(
+    "--overwrite",
+    dest="overwrite",
+    action="store_true",
+    help="Allow incoming messages to overwrite existing entries (can result in 
data loss)",
+)
+parser.add_argument(
     "--ignorebody",
     dest="ibody",
     type=str,
@@ -901,9 +936,15 @@ if dumpfile:
     dumpfile.write("]\n")
     dumpfile.close()
 
-print(
-    "All done! %u records inserted/updated after %u seconds. %u records were 
bad and ignored"
-    % (goodies, int(time.time() - start), baddies)
-)
+if args.overwrite:
+    print(
+        "All done! %u records processed (including %u replacements) after %u 
seconds. %u records were bad and ignored."
+        % (goodies, replacements, int(time.time() - start), baddies)
+    )    
+else:
+    print(
+        "All done! %u records inserted after %u seconds. %u records were bad 
and ignored. %u duplicates were ignored."
+        % (goodies, int(time.time() - start), baddies, dupes)
+    )
 if dedupped > 0:
     print("%u records were not inserted due to deduplication" % dedupped)

Reply via email to