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);