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

ashishvijaywargiya pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-plugins.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 76198c6d8 Fix Lucene IndexWriter write.lock conflicts during 
testIntegration (#366)
76198c6d8 is described below

commit 76198c6d8e39e29262be7c59531158d1ce080489
Author: Ashish Vijaywargiya <[email protected]>
AuthorDate: Sun Aug 16 14:53:07 2026 +0530

    Fix Lucene IndexWriter write.lock conflicts during testIntegration (#366)
    
    During `testIntegration`, each test suite gets its own uniquely-named
    delegator, and `DocumentIndexer.getInstance()` keyed its singleton map
    by `delegatorName + "_" + indexName`. Since all of these delegators
    point at the same physical Lucene index path, every suite spawned its
    own `DocumentIndexer` thread competing for the same on-disk
    `write.lock`.
    
    That contention was made worse by two other bugs:
    `ProductDocument.prepareDocument()` could pass a null
    `productFeatureCategoryId` into Lucene's `StringField`, which requires
    non-null values, throwing an uncaught `IllegalArgumentException`; and
    `DocumentIndexer.run()` called `prepareDocument()` outside any
    try/catch, so that exception killed the indexer thread while its
    `IndexWriter` was still open, leaving the write lock held with nothing
    left alive to release it. Once the first thread died holding the lock,
    every later thread failed immediately with `LockObtainFailedException`.
    
    Changes:
    - `ProductDocument.java` — guard the three feature fields with the
    class's existing `checkValue()` helper (already used elsewhere in the
    same file) instead of passing raw nullable values into `StringField`.
    - `DocumentIndexer.java` — key the singleton map on
    `SearchWorker.getIndexPath(indexName)` (the resolved physical path)
    instead of delegator name, with an `!isAlive()` check so a dead thread
    gets replaced instead of cached forever; wrap the indexing loop in
    try/finally so `IndexWriter.close()` runs on any exit path.
    
    Verified with a full `./gradlew testIntegration` run: no more
    lock-related errors, and indexing completes successfully (309
    index/delete operations logged in this run) with no failing suites.
---
 .../ofbiz/content/search/DocumentIndexer.java      | 112 ++++++++++-----------
 .../ofbiz/content/search/ProductDocument.java      |  10 +-
 2 files changed, 59 insertions(+), 63 deletions(-)

diff --git 
a/lucene/src/main/java/org/apache/ofbiz/content/search/DocumentIndexer.java 
b/lucene/src/main/java/org/apache/ofbiz/content/search/DocumentIndexer.java
index a00e8cd36..ec74e7f5c 100644
--- a/lucene/src/main/java/org/apache/ofbiz/content/search/DocumentIndexer.java
+++ b/lucene/src/main/java/org/apache/ofbiz/content/search/DocumentIndexer.java
@@ -62,9 +62,9 @@ public final class DocumentIndexer extends Thread {
     }
 
     public static synchronized DocumentIndexer getInstance(Delegator 
delegator, String indexName) {
-        String documentIndexerId = delegator.getDelegatorName() + "_" + 
indexName;
+        String documentIndexerId = SearchWorker.getIndexPath(indexName);
         DocumentIndexer documentIndexer = 
documentIndexerMap.get(documentIndexerId);
-        if (documentIndexer == null) {
+        if (documentIndexer == null || !documentIndexer.isAlive()) {
             documentIndexer = new DocumentIndexer(delegator, indexName);
             documentIndexer.setName("DocumentIndexer_" + 
delegator.getDelegatorName() + "_" + indexName);
             documentIndexer.start();
@@ -77,79 +77,73 @@ public final class DocumentIndexer extends Thread {
     public void run() {
         IndexWriter indexWriter = null;
         int uncommittedDocs = 0;
-        while (true) {
-            LuceneDocument ofbizDocument;
-            try {
-                // Execution will pause here until the queue receives a 
LuceneDocument for indexing
-                ofbizDocument = documentIndexQueue.take();
-            } catch (InterruptedException e) {
-                Debug.logError(e, MODULE);
-                if (indexWriter != null) {
+        try {
+            while (true) {
+                LuceneDocument ofbizDocument;
+                try {
+                    // Execution will pause here until the queue receives a 
LuceneDocument for indexing
+                    ofbizDocument = documentIndexQueue.take();
+                } catch (InterruptedException e) {
+                    Debug.logError(e, MODULE);
+                    break;
+                }
+                Term documentIdentifier = 
ofbizDocument.getDocumentIdentifier();
+                Document document = 
ofbizDocument.prepareDocument(this.delegator);
+                if (indexWriter == null) {
                     try {
-                        indexWriter.close();
-                        indexWriter = null;
-                    } catch (IOException ioe) {
-                        Debug.logError(ioe, MODULE);
+                        StandardAnalyzer analyzer = new StandardAnalyzer();
+                        indexWriter = new IndexWriter(this.indexDirectory, new 
IndexWriterConfig(analyzer));
+                    } catch (CorruptIndexException e) {
+                        Debug.logError("Corrupted lucene index: " + 
e.getMessage(), MODULE);
+                        break;
+                    } catch (LockObtainFailedException e) {
+                        Debug.logError("Could not obtain Lock on lucene index 
" + e.getMessage(), MODULE);
+                        // TODO: put the thread to sleep waiting for the 
locked to be released
+                        break;
+                    } catch (IOException e) {
+                        Debug.logError(e.getMessage(), MODULE);
+                        break;
                     }
                 }
-                break;
-            }
-            Term documentIdentifier = ofbizDocument.getDocumentIdentifier();
-            Document document = ofbizDocument.prepareDocument(this.delegator);
-            if (indexWriter == null) {
                 try {
-                    StandardAnalyzer analyzer = new StandardAnalyzer();
-                    indexWriter = new IndexWriter(this.indexDirectory, new 
IndexWriterConfig(analyzer));
-                } catch (CorruptIndexException e) {
-                    Debug.logError("Corrupted lucene index: " + 
e.getMessage(), MODULE);
-                    break;
-                } catch (LockObtainFailedException e) {
-                    Debug.logError("Could not obtain Lock on lucene index " + 
e.getMessage(), MODULE);
-                    // TODO: put the thread to sleep waiting for the locked to 
be released
-                    break;
-                } catch (IOException e) {
-                    Debug.logError(e.getMessage(), MODULE);
-                    break;
-                }
-            }
-            try {
-                if (document == null) {
-                    indexWriter.deleteDocuments(documentIdentifier);
-                    if (Debug.infoOn()) {
-                        Debug.logInfo(getName() + ": deleted Lucene document: 
" + ofbizDocument, MODULE);
+                    if (document == null) {
+                        indexWriter.deleteDocuments(documentIdentifier);
+                        if (Debug.infoOn()) {
+                            Debug.logInfo(getName() + ": deleted Lucene 
document: " + ofbizDocument, MODULE);
+                        }
+                    } else {
+                        indexWriter.updateDocument(documentIdentifier, 
document);
+                        if (Debug.infoOn()) {
+                            Debug.logInfo(getName() + ": indexed Lucene 
document: " + ofbizDocument, MODULE);
+                        }
                     }
-                } else {
-                    indexWriter.updateDocument(documentIdentifier, document);
-                    if (Debug.infoOn()) {
-                        Debug.logInfo(getName() + ": indexed Lucene document: 
" + ofbizDocument, MODULE);
+                } catch (Exception e) {
+                    Debug.logError(e, getName() + ": error processing Lucene 
document: " + ofbizDocument, MODULE);
+                    continue;
+                }
+                uncommittedDocs++;
+                if (uncommittedDocs == UNCOMMITTED_DOC_LIMIT || 
documentIndexQueue.peek() == null) {
+                    // limit reached or queue empty, time to commit
+                    try {
+                        indexWriter.commit();
+                    } catch (IOException e) {
+                        Debug.logError(e, MODULE);
                     }
+                    uncommittedDocs = 0;
                 }
-            } catch (Exception e) {
-                Debug.logError(e, getName() + ": error processing Lucene 
document: " + ofbizDocument, MODULE);
                 if (documentIndexQueue.peek() == null) {
                     try {
                         indexWriter.close();
                         indexWriter = null;
-                    } catch (IOException ioe) {
-                        Debug.logError(ioe, MODULE);
+                    } catch (IOException e) {
+                        Debug.logError(e, MODULE);
                     }
                 }
-                continue;
-            }
-            uncommittedDocs++;
-            if (uncommittedDocs == UNCOMMITTED_DOC_LIMIT || 
documentIndexQueue.peek() == null) {
-                // limit reached or queue empty, time to commit
-                try {
-                    indexWriter.commit();
-                } catch (IOException e) {
-                    Debug.logError(e, MODULE);
-                }
-                uncommittedDocs = 0;
             }
-            if (documentIndexQueue.peek() == null) {
+        } finally {
+            if (indexWriter != null) {
                 try {
                     indexWriter.close();
-                    indexWriter = null;
                 } catch (IOException e) {
                     Debug.logError(e, MODULE);
                 }
diff --git 
a/lucene/src/main/java/org/apache/ofbiz/content/search/ProductDocument.java 
b/lucene/src/main/java/org/apache/ofbiz/content/search/ProductDocument.java
index 82f33ca20..f3ed1700b 100644
--- a/lucene/src/main/java/org/apache/ofbiz/content/search/ProductDocument.java
+++ b/lucene/src/main/java/org/apache/ofbiz/content/search/ProductDocument.java
@@ -193,10 +193,12 @@ public class ProductDocument implements LuceneDocument {
                         } else if (thruDate != null) {
                             nextReIndex = checkSetNextReIndex(thruDate, 
nextReIndex);
                         }
-                        doc.add(new StringField("productFeatureId", 
productFeatureAndAppl.getString("productFeatureId"), Field.Store.NO));
-                        doc.add(new StringField("productFeatureCategoryId", 
productFeatureAndAppl.getString("productFeatureCategoryId"),
-                                Field.Store.NO));
-                        doc.add(new StringField("productFeatureTypeId", 
productFeatureAndAppl.getString("productFeatureTypeId"), Field.Store.NO));
+                        String featureId = 
checkValue(productFeatureAndAppl.getString("productFeatureId"));
+                        doc.add(new StringField("productFeatureId", featureId, 
Field.Store.NO));
+                        String featureCategoryId = 
checkValue(productFeatureAndAppl.getString("productFeatureCategoryId"));
+                        doc.add(new StringField("productFeatureCategoryId", 
featureCategoryId, Field.Store.NO));
+                        String featureTypeId = 
checkValue(productFeatureAndAppl.getString("productFeatureTypeId"));
+                        doc.add(new StringField("productFeatureTypeId", 
featureTypeId, Field.Store.NO));
                         addTextField(doc, "featureDescription", 
productFeatureAndAppl.getString("description"), false, "fullText", delegator);
                         addTextField(doc, "featureAbbreviation", 
productFeatureAndAppl.getString("abbrev"), false, "fullText", delegator);
                         addTextField(doc, "featureCode", 
productFeatureAndAppl.getString("idCode"), false, "fullText", delegator);

Reply via email to