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

epugh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr-mcp.git


The following commit(s) were added to refs/heads/main by this push:
     new 48953297 perf(indexing): one request per JSON/markdown call; retry per 
document only on a 400 (#233)
48953297 is described below

commit 48953297a45d7dc5a83f5cad99a91ace8ffa550c
Author: Aditya Parikh <[email protected]>
AuthorDate: Tue Oct 6 14:50:30 2026 -0400

    perf(indexing): one request per JSON/markdown call; retry per document only 
on a 400 (#233)
    
    * perf(indexing): cut redundant Solr round trips in indexDocuments
    
    indexDocuments() retried every document individually on ANY batch
    failure (SolrServerException/IOException/RuntimeException), swallowing
    the cause and costing up to N+2 round trips per batch on a missing
    collection, auth failure, 5xx, or Solr being down -- ending as
    "Successfully indexed 0 of N" instead of reporting the real error.
    Per-document fallback now only fires for a Solr 400 (bad documents);
    everything else propagates immediately with the root cause intact.
    
    JSON/markdown indexing also issued a separate solrClient.commit() call
    after the adds. The last batch now carries add + commit on a single
    UpdateRequest (same soft-commit semantics: waitSearcher=true,
    searchable on return), matching how CSV/XML already ride the commit on
    their forwarded request. Markdown (always one document) and JSON up to
    the batch size go from two round trips to one. A batch that falls back
    to per-document retry still commits explicitly, since its embedded
    commit never ran.
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Aditya Parikh <[email protected]>
    
    * refactor(indexing): send JSON and markdown documents in one request
    
    Replace the client-side 1000-document batching with one UpdateRequest
    carrying the documents and the soft commit, as CSV and XML already
    send their whole payload in one request. The documents arrive in a
    single MCP call and are already in memory, so batching bought nothing
    and needed last-batch bookkeeping to place the commit.
    
    A 400 still falls back to per-document adds followed by a commit.
    The per-call field-name memo in JsonDocumentCreator is reverted: it
    saved CPU only, not Solr round trips.
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Aditya Parikh <[email protected]>
    
    ---------
    
    Signed-off-by: Aditya Parikh <[email protected]>
    Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
 .../solr/mcp/server/indexing/IndexingService.java  | 142 ++++++-------------
 .../mcp/server/indexing/IndexingServiceTest.java   | 152 ++++++++++-----------
 2 files changed, 114 insertions(+), 180 deletions(-)

diff --git 
a/src/main/java/org/apache/solr/mcp/server/indexing/IndexingService.java 
b/src/main/java/org/apache/solr/mcp/server/indexing/IndexingService.java
index e5ae85f2..2d6fdc20 100644
--- a/src/main/java/org/apache/solr/mcp/server/indexing/IndexingService.java
+++ b/src/main/java/org/apache/solr/mcp/server/indexing/IndexingService.java
@@ -27,8 +27,10 @@ import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
 import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest;
+import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.client.solrj.response.UpdateResponse;
 import org.apache.solr.client.solrj.util.ClientUtils;
+import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrInputDocument;
 import org.apache.solr.common.util.ContentStreamBase;
 import 
org.apache.solr.mcp.server.indexing.documentcreator.IndexingDocumentCreator;
@@ -50,9 +52,9 @@ import org.springframework.stereotype.Service;
  * <p>
  * This service handles the conversion of JSON, CSV, XML, and markdown 
documents
  * into Solr-compatible format and manages the indexing process with robust
- * error handling and batch processing capabilities. It employs a schema-less
- * approach where Solr automatically detects field types, eliminating the need
- * for predefined schema configuration.
+ * error handling. It employs a schema-less approach where Solr automatically
+ * detects field types, eliminating the need for predefined schema
+ * configuration.
  *
  * <p>
  * <strong>Core Features:</strong>
@@ -68,10 +70,8 @@ import org.springframework.stereotype.Service;
  * forwarded to Solr's XML update handler
  * <li><strong>Markdown Processing</strong>: Support for markdown documents 
with
  * front matter, title, and heading extraction
- * <li><strong>Batch Processing</strong>: Efficient bulk indexing with
- * configurable batch sizes
- * <li><strong>Error Resilience</strong>: Individual document fallback when
- * batch operations fail
+ * <li><strong>Error Resilience</strong>: Individual document fallback when 
Solr
+ * rejects a document
  * <li><strong>Field Names</strong>: Used as given in every format; nested JSON
  * objects are flattened with underscores
  * </ul>
@@ -93,13 +93,12 @@ import org.springframework.stereotype.Service;
  * arrays by converting them to multi-valued fields that Solr natively 
supports.
  *
  * <p>
- * <strong>Batch Processing Strategy:</strong>
+ * <strong>Error Recovery:</strong>
  *
  * <p>
- * Uses configurable batch sizes (default 1000 documents) for optimal
- * performance. If a batch fails, the service automatically retries by indexing
- * documents individually to identify and skip problematic documents while
- * preserving valid ones.
+ * JSON and markdown documents are sent in one update request. If Solr rejects
+ * it because of a bad document, the service retries the documents individually
+ * to skip the problematic ones while preserving valid ones.
  *
  * <p>
  * <strong>Example Usage:</strong>
@@ -124,8 +123,6 @@ public class IndexingService {
 
        private static final Logger logger = 
LoggerFactory.getLogger(IndexingService.class);
 
-       private static final int DEFAULT_BATCH_SIZE = 1000;
-
        /** SolrJ client for communicating with Solr server */
        private final SolrClient solrClient;
 
@@ -179,7 +176,7 @@ public class IndexingService {
         * <ol>
         * <li>Parse JSON string into structured documents
         * <li>Convert to schema-less SolrInputDocument objects
-        * <li>Execute batch indexing with error handling
+        * <li>Index them in one request, with error handling
         * <li>Commit changes to make documents searchable
         * </ol>
         *
@@ -428,107 +425,52 @@ public class IndexingService {
        }
 
        /**
-        * Indexes a list of SolrInputDocument objects into a Solr collection 
using
-        * batch processing.
-        *
-        * <p>
-        * This method implements a robust batch indexing strategy that 
optimizes
-        * performance while providing resilience against individual document 
failures.
-        * It processes documents in configurable batches and includes fallback
-        * mechanisms for error recovery.
-        *
-        * <p>
-        * <strong>Batch Processing Strategy:</strong>
-        *
-        * <ul>
-        * <li><strong>Batch Size</strong>: Configurable (default 1000) for 
optimal
-        * performance
-        * <li><strong>Error Recovery</strong>: Individual document retry on 
batch
-        * failure
-        * <li><strong>Success Tracking</strong>: Accurate count of 
successfully indexed
-        * documents
-        * <li><strong>Commit Strategy</strong>: Single soft commit after all 
batches
-        * for consistency
-        * </ul>
-        *
-        * <p>
-        * <strong>Error Handling Workflow:</strong>
-        *
-        * <ol>
-        * <li>Attempt batch indexing for optimal performance
-        * <li>On batch failure, retry each document individually
-        * <li>Track successful vs failed document counts
-        * <li>Continue processing remaining batches despite failures
-        * <li>Commit all successful changes at the end
-        * </ol>
-        *
-        * <p>
-        * <strong>Performance Considerations:</strong>
+        * Indexes documents into a Solr collection in one update request that 
also
+        * carries the soft commit, as {@link #forward} does for CSV and XML, 
so the
+        * documents are searchable when this returns.
         *
         * <p>
-        * Batch processing significantly improves indexing performance 
compared to
-        * individual document operations. The fallback to individual 
processing ensures
-        * maximum document ingestion even when some documents have issues.
-        *
-        * <p>
-        * <strong>Transaction Behavior:</strong>
-        *
-        * <p>
-        * The method soft-commits after all batches are processed, making 
indexed
-        * documents immediately searchable. This ensures atomicity at the 
operation
-        * level while maintaining performance through batching.
+        * If Solr rejects the request with a 400 (a document with an unknown 
field or a
+        * value of the wrong type), the documents are retried one at a time so 
the
+        * valid ones are still indexed, then committed. Any other failure 
(unknown
+        * collection, auth, 5xx, Solr unreachable) would fail every retry the 
same way,
+        * so it propagates unchanged.
         *
         * @param collection
         *            the name of the Solr collection to index into
         * @param documents
-        *            list of SolrInputDocument objects to index
+        *            the documents to index
         * @return the number of documents successfully indexed
         * @throws SolrServerException
-        *             if there are critical errors in Solr communication
+        *             if Solr cannot be reached or the commit fails
         * @throws IOException
-        *             if there are critical errors in commit operations
-        * @see SolrInputDocument
-        * @see SolrClient#add(String, java.util.Collection)
-        * @see SolrClient#commit(String, boolean, boolean, boolean)
+        *             if there are I/O errors during communication
         */
        public int indexDocuments(String collection, List<SolrInputDocument> 
documents)
                        throws SolrServerException, IOException {
-               int successCount = 0;
-               final int batchSize = DEFAULT_BATCH_SIZE;
-
-               for (int i = 0; i < documents.size(); i += batchSize) {
-                       final int endIndex = Math.min(i + batchSize, 
documents.size());
-                       final List<SolrInputDocument> batch = 
documents.subList(i, endIndex);
-
-                       try {
-                               solrClient.add(collection, batch);
-                               successCount += batch.size();
-                       } catch (SolrServerException | IOException | 
RuntimeException e) {
-                               logger.warn("Batch indexing failed, retrying 
individually", e);
-                               // Try indexing documents individually to 
identify problematic ones
-                               for (SolrInputDocument doc : batch) {
-                                       try {
-                                               solrClient.add(collection, doc);
-                                               successCount++;
-                                       } catch (SolrServerException | 
IOException | RuntimeException e2) {
-                                               logger.debug("Failed to index 
individual document", e2);
-                                               // Document failed to index - 
this is expected behavior for problematic
-                                               // documents
-                                               // We continue processing the 
rest of the batch
-                                       }
-                               }
+               UpdateRequest request = new UpdateRequest();
+               request.add(documents);
+               request.setAction(AbstractUpdateRequest.ACTION.COMMIT, false, 
true, true);
+               try {
+                       request.process(solrClient, collection);
+                       return documents.size();
+               } catch (SolrException e) {
+                       if (e.code() != 
SolrException.ErrorCode.BAD_REQUEST.code) {
+                               throw e;
                        }
+                       logger.warn("Solr rejected the batch, retrying 
documents individually", e);
                }
 
-               try {
-                       // waitFlush=false, waitSearcher=true, softCommit=true: 
the documents are
-                       // searchable when this method returns, while the hard 
commit (segment fsync)
-                       // is left to Solr's autoCommit, so many small calls do 
not each force one.
-                       solrClient.commit(collection, false, true, true);
-               } catch (SolrServerException | IOException e) {
-                       logger.error("Failed to commit after indexing to 
collection: {}", collection, e);
-                       throw e;
+               int successCount = 0;
+               for (SolrInputDocument doc : documents) {
+                       try {
+                               solrClient.add(collection, doc);
+                               successCount++;
+                       } catch (SolrException e) {
+                               logger.debug("Failed to index individual 
document", e);
+                       }
                }
+               solrClient.commit(collection, false, true, true);
                return successCount;
        }
 
diff --git 
a/src/test/java/org/apache/solr/mcp/server/indexing/IndexingServiceTest.java 
b/src/test/java/org/apache/solr/mcp/server/indexing/IndexingServiceTest.java
index cb42cdd8..3a9db4a8 100644
--- a/src/test/java/org/apache/solr/mcp/server/indexing/IndexingServiceTest.java
+++ b/src/test/java/org/apache/solr/mcp/server/indexing/IndexingServiceTest.java
@@ -26,9 +26,11 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
+import org.apache.solr.client.solrj.RemoteSolrException;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest;
+import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.common.SolrInputDocument;
 import org.apache.solr.common.util.NamedList;
 import 
org.apache.solr.mcp.server.indexing.documentcreator.DocumentProcessingException;
@@ -71,14 +73,19 @@ class IndexingServiceTest {
                List<Map<String, Object>> json = List.of(Map.of("id", "1", 
"title", "Test"));
                List<SolrInputDocument> mockDocs = createMockDocuments(1);
                
when(indexingDocumentCreator.createSchemalessDocumentsFromJson(json)).thenReturn(mockDocs);
-               when(solrClient.add(eq("test_collection"), 
any(Collection.class))).thenReturn(null);
-               when(solrClient.commit("test_collection", false, true, 
true)).thenReturn(null);
+               when(solrClient.request(any(UpdateRequest.class), 
eq("test_collection"))).thenReturn(new NamedList<>());
 
                indexingService.indexJsonDocuments("test_collection", json);
 
                
verify(indexingDocumentCreator).createSchemalessDocumentsFromJson(json);
-               verify(solrClient).add(eq("test_collection"), 
any(Collection.class));
-               verify(solrClient).commit("test_collection", false, true, true);
+               // the add and the commit ride on one request
+               ArgumentCaptor<UpdateRequest> captor = 
ArgumentCaptor.forClass(UpdateRequest.class);
+               verify(solrClient).request(captor.capture(), 
eq("test_collection"));
+               assertEquals(1, captor.getValue().getDocuments().size());
+               assertEquals("true", 
captor.getValue().getParams().get("commit"));
+               assertEquals("true", 
captor.getValue().getParams().get("softCommit"));
+               verify(solrClient, never()).add(anyString(), 
any(Collection.class));
+               verify(solrClient, never()).commit(anyString(), anyBoolean(), 
anyBoolean(), anyBoolean());
        }
 
        @Test
@@ -97,119 +104,109 @@ class IndexingServiceTest {
        @Test
        void indexDocuments_WithSmallBatch_ShouldIndexSuccessfully() throws 
Exception {
                List<SolrInputDocument> docs = createMockDocuments(5);
-               when(solrClient.add(eq("test_collection"), 
any(Collection.class))).thenReturn(null);
-               when(solrClient.commit("test_collection", false, true, 
true)).thenReturn(null);
+               when(solrClient.request(any(UpdateRequest.class), 
eq("test_collection"))).thenReturn(new NamedList<>());
 
                int result = indexingService.indexDocuments("test_collection", 
docs);
 
                assertEquals(5, result);
-               verify(solrClient).add(eq("test_collection"), 
any(Collection.class));
-               verify(solrClient).commit("test_collection", false, true, true);
+               ArgumentCaptor<UpdateRequest> captor = 
ArgumentCaptor.forClass(UpdateRequest.class);
+               verify(solrClient).request(captor.capture(), 
eq("test_collection"));
+               assertEquals(5, captor.getValue().getDocuments().size());
+               assertEquals("true", 
captor.getValue().getParams().get("commit"));
+               verify(solrClient, never()).add(anyString(), 
any(Collection.class));
+               verify(solrClient, never()).commit(anyString(), anyBoolean(), 
anyBoolean(), anyBoolean());
        }
 
        @Test
-       void indexDocuments_WithLargeBatch_ShouldProcessInBatches() throws 
Exception {
+       void indexDocuments_WithManyDocuments_SendsOneRequest() throws 
Exception {
                List<SolrInputDocument> docs = createMockDocuments(2500);
-               when(solrClient.add(eq("test_collection"), 
any(Collection.class))).thenReturn(null);
-               when(solrClient.commit(eq("test_collection"), eq(false), 
eq(true), eq(true))).thenReturn(null);
+               when(solrClient.request(any(UpdateRequest.class), 
eq("test_collection"))).thenReturn(new NamedList<>());
 
                int result = indexingService.indexDocuments("test_collection", 
docs);
 
                assertEquals(2500, result);
-               verify(solrClient, times(3)).add(eq("test_collection"), 
any(Collection.class));
-               verify(solrClient).commit("test_collection", false, true, true);
+               ArgumentCaptor<UpdateRequest> captor = 
ArgumentCaptor.forClass(UpdateRequest.class);
+               verify(solrClient).request(captor.capture(), 
eq("test_collection"));
+               assertEquals(2500, captor.getValue().getDocuments().size());
+               verify(solrClient, never()).add(anyString(), 
any(Collection.class));
        }
-
        @Test
-       void indexDocuments_WhenBatchFails_ShouldRetryIndividually() throws 
Exception {
+       void indexDocuments_WhenSolrRejectsTheRequest_ShouldRetryIndividually() 
throws Exception {
                List<SolrInputDocument> docs = createMockDocuments(3);
-
-               when(solrClient.add(eq("test_collection"), 
any(List.class))).thenThrow(new SolrServerException("Batch error"));
-
+               when(solrClient.request(any(UpdateRequest.class), 
eq("test_collection")))
+                               .thenThrow(new 
RemoteSolrException("http://localhost:8983/solr";, 400, "Batch error", null));
                when(solrClient.add(eq("test_collection"), 
any(SolrInputDocument.class))).thenReturn(null);
-               when(solrClient.commit("test_collection", false, true, 
true)).thenReturn(null);
 
                int result = indexingService.indexDocuments("test_collection", 
docs);
 
                assertEquals(3, result);
-               verify(solrClient).add(eq("test_collection"), 
any(Collection.class));
                verify(solrClient, times(3)).add(eq("test_collection"), 
any(SolrInputDocument.class));
+               // the rejected request's commit never ran
                verify(solrClient).commit("test_collection", false, true, true);
        }
-
        @Test
        void 
indexDocuments_WhenSomeIndividualDocumentsFail_ShouldIndexSuccessfulOnes() 
throws Exception {
                List<SolrInputDocument> docs = createMockDocuments(3);
-
-               when(solrClient.add(eq("test_collection"), 
any(List.class))).thenThrow(new SolrServerException("Batch error"));
-
-               when(solrClient.add(eq("test_collection"), 
any(SolrInputDocument.class))).thenReturn(null)
-                               .thenThrow(new SolrServerException("Document 
error")).thenReturn(null);
-
-               when(solrClient.commit("test_collection", false, true, 
true)).thenReturn(null);
+               RemoteSolrException badRequest = new 
RemoteSolrException("http://localhost:8983/solr";, 400, "Bad doc", null);
+               when(solrClient.request(any(UpdateRequest.class), 
eq("test_collection"))).thenThrow(badRequest);
+               when(solrClient.add(eq("test_collection"), 
any(SolrInputDocument.class))).thenReturn(null).thenThrow(badRequest)
+                               .thenReturn(null);
 
                int result = indexingService.indexDocuments("test_collection", 
docs);
 
                assertEquals(2, result);
-               verify(solrClient).add(eq("test_collection"), 
any(Collection.class));
                verify(solrClient, times(3)).add(eq("test_collection"), 
any(SolrInputDocument.class));
                verify(solrClient).commit("test_collection", false, true, true);
        }
-
-       @Test
-       void indexDocuments_WithEmptyList_ShouldStillCommit() throws Exception {
-               List<SolrInputDocument> emptyDocs = new ArrayList<>();
-               when(solrClient.commit("test_collection", false, true, 
true)).thenReturn(null);
-
-               int result = indexingService.indexDocuments("test_collection", 
emptyDocs);
-
-               assertEquals(0, result);
-               verify(solrClient, never()).add(anyString(), any(List.class));
-               verify(solrClient).commit("test_collection", false, true, true);
-       }
-
        @Test
-       void indexDocuments_WhenCommitFails_ShouldPropagateException() throws 
Exception {
+       void indexDocuments_WhenTheRequestFails_ShouldPropagateException() 
throws Exception {
                List<SolrInputDocument> docs = createMockDocuments(2);
-               when(solrClient.add(eq("test_collection"), 
any(Collection.class))).thenReturn(null);
-               when(solrClient.commit("test_collection", false, true, 
true)).thenThrow(new IOException("Commit failed"));
+               when(solrClient.request(any(UpdateRequest.class), 
eq("test_collection")))
+                               .thenThrow(new IOException("Commit failed"));
 
                assertThrows(IOException.class, () -> {
                        indexingService.indexDocuments("test_collection", docs);
                });
-               verify(solrClient).add(eq("test_collection"), 
any(Collection.class));
-               verify(solrClient).commit("test_collection", false, true, true);
+               verify(solrClient).request(any(UpdateRequest.class), 
eq("test_collection"));
+               // not a Solr 400: no per-document fallback
+               verify(solrClient, never()).add(anyString(), 
any(SolrInputDocument.class));
+               verify(solrClient, never()).commit(anyString(), anyBoolean(), 
anyBoolean(), anyBoolean());
        }
 
        @Test
        void 
indexDocuments_SoftCommitsSoDocumentsAreSearchableWithoutForcingAnFsync() 
throws Exception {
                List<SolrInputDocument> docs = createMockDocuments(2);
-               when(solrClient.add(eq("test_collection"), 
any(Collection.class))).thenReturn(null);
+               when(solrClient.request(any(UpdateRequest.class), 
eq("test_collection"))).thenReturn(new NamedList<>());
 
                indexingService.indexDocuments("test_collection", docs);
 
-               // waitFlush=false, waitSearcher=true, softCommit=true. 
waitSearcher is what
-               // keeps the documents searchable the moment the tool returns; 
softCommit is
-               // what avoids an fsync per call. Measured against Solr: 8.6 ms 
versus 18.9 ms
-               // for a hard commit, and a p90 of 10.7 ms versus 41.3 ms.
-               verify(solrClient).commit("test_collection", false, true, true);
+               // waitFlush=false, waitSearcher=true, softCommit=true, on the 
add request.
+               // waitSearcher is what keeps the documents searchable the 
moment the tool
+               // returns; softCommit is what avoids an fsync per call. 
Measured against
+               // Solr: 8.6 ms versus 18.9 ms for a hard commit, and a p90 of 
10.7 ms versus
+               // 41.3 ms.
+               ArgumentCaptor<UpdateRequest> captor = 
ArgumentCaptor.forClass(UpdateRequest.class);
+               verify(solrClient).request(captor.capture(), 
eq("test_collection"));
+               assertEquals("true", 
captor.getValue().getParams().get("commit"));
+               assertEquals("true", 
captor.getValue().getParams().get("softCommit"));
+               assertEquals("true", 
captor.getValue().getParams().get("waitSearcher"));
+               verify(solrClient, never()).commit(anyString(), anyBoolean(), 
anyBoolean(), anyBoolean());
                verify(solrClient, never()).commit(anyString());
        }
 
        @Test
-       void indexDocuments_ShouldBatchCorrectly() throws Exception {
-               List<SolrInputDocument> docs = createMockDocuments(1000);
-               when(solrClient.add(eq("test_collection"), 
any(Collection.class))).thenReturn(null);
-               when(solrClient.commit("test_collection", false, true, 
true)).thenReturn(null);
+       void 
indexDocuments_WhenCollectionIsMissing_ShouldPropagateWithoutRetrying() throws 
Exception {
+               List<SolrInputDocument> docs = createMockDocuments(3);
 
-               int result = indexingService.indexDocuments("test_collection", 
docs);
+               when(solrClient.request(any(UpdateRequest.class), 
eq("test_collection")))
+                               .thenThrow(new 
RemoteSolrException("http://localhost:8983/solr";, 404, "Collection not found", 
null));
 
-               assertEquals(1000, result);
+               assertThrows(RemoteSolrException.class, () -> 
indexingService.indexDocuments("test_collection", docs));
 
-               ArgumentCaptor<Collection<SolrInputDocument>> captor = 
ArgumentCaptor.forClass(Collection.class);
-               verify(solrClient).add(eq("test_collection"), captor.capture());
-               assertEquals(1000, captor.getValue().size());
-               verify(solrClient).commit("test_collection", false, true, true);
+               verify(solrClient, times(1)).request(any(UpdateRequest.class), 
eq("test_collection"));
+               verify(solrClient, never()).add(anyString(), 
any(SolrInputDocument.class));
+               verify(solrClient, never()).add(anyString(), 
any(Collection.class));
+               verify(solrClient, never()).commit(anyString(), anyBoolean(), 
anyBoolean(), anyBoolean());
        }
 
        @Test
@@ -217,16 +214,15 @@ class IndexingServiceTest {
                List<Map<String, Object>> json = List.of(Map.of("id", "1"));
                List<SolrInputDocument> mockDocs = createMockDocuments(1);
                
when(indexingDocumentCreator.createSchemalessDocumentsFromJson(json)).thenReturn(mockDocs);
-               when(solrClient.add(eq("test_collection"), any(List.class)))
-                               .thenThrow(new SolrServerException("Solr 
connection error"));
-               when(solrClient.add(eq("test_collection"), 
any(SolrInputDocument.class)))
+               // Solr unreachable: propagate rather than retry and report "0 
of 1"
+               when(solrClient.request(any(UpdateRequest.class), 
eq("test_collection")))
                                .thenThrow(new SolrServerException("Solr 
connection error"));
-               when(solrClient.commit("test_collection", false, true, 
true)).thenReturn(null);
 
-               indexingService.indexJsonDocuments("test_collection", json);
+               assertThrows(SolrServerException.class, () -> 
indexingService.indexJsonDocuments("test_collection", json));
 
-               verify(solrClient).add(eq("test_collection"), any(List.class));
-               verify(solrClient).add(eq("test_collection"), 
any(SolrInputDocument.class));
+               verify(solrClient).request(any(UpdateRequest.class), 
eq("test_collection"));
+               verify(solrClient, never()).add(anyString(), 
any(SolrInputDocument.class));
+               verify(solrClient, never()).commit(anyString(), anyBoolean(), 
anyBoolean(), anyBoolean());
        }
 
        @Test
@@ -300,21 +296,17 @@ class IndexingServiceTest {
        }
 
        @Test
-       void indexDocuments_WithRuntimeException_ShouldRetryIndividually() 
throws Exception {
+       void 
indexDocuments_WithRuntimeException_ShouldPropagateWithoutRetrying() throws 
Exception {
                List<SolrInputDocument> docs = createMockDocuments(2);
 
-               when(solrClient.add(eq("test_collection"), any(List.class)))
+               when(solrClient.request(any(UpdateRequest.class), 
eq("test_collection")))
                                .thenThrow(new RuntimeException("Unexpected 
error"));
 
-               when(solrClient.add(eq("test_collection"), 
any(SolrInputDocument.class))).thenReturn(null);
-               when(solrClient.commit("test_collection", false, true, 
true)).thenReturn(null);
+               assertThrows(RuntimeException.class, () -> 
indexingService.indexDocuments("test_collection", docs));
 
-               int result = indexingService.indexDocuments("test_collection", 
docs);
-
-               assertEquals(2, result);
-               verify(solrClient).add(eq("test_collection"), 
any(Collection.class));
-               verify(solrClient, times(2)).add(eq("test_collection"), 
any(SolrInputDocument.class));
-               verify(solrClient).commit("test_collection", false, true, true);
+               verify(solrClient).request(any(UpdateRequest.class), 
eq("test_collection"));
+               verify(solrClient, never()).add(anyString(), 
any(SolrInputDocument.class));
+               verify(solrClient, never()).commit(anyString(), anyBoolean(), 
anyBoolean(), anyBoolean());
        }
 
        private List<SolrInputDocument> createMockDocuments(int count) {

Reply via email to