This is an automated email from the ASF dual-hosted git repository. reschke pushed a commit to branch OAK-10127-backport in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
commit 010fac671bc4d7551ce2247e391e62e164bc76fb Author: Julian Reschke <[email protected]> AuthorDate: Wed Feb 18 16:55:42 2026 +0100 OAK-10127: Log warn message when MongoDB document is big (backport to 1.22) --- .../plugins/document/mongo/MongoDocumentStore.java | 21 +++ .../document/mongo/MongoDBExceptionTest.java | 141 +++++++++++++++++++++ 2 files changed, 162 insertions(+) diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java index 2ea202891d..4b42d3f53b 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java @@ -45,6 +45,9 @@ import com.google.common.util.concurrent.UncheckedExecutionException; import com.mongodb.Block; import com.mongodb.DBObject; import com.mongodb.MongoBulkWriteException; +import com.mongodb.MongoWriteException; +import com.mongodb.MongoCommandException; +import com.mongodb.WriteError; import com.mongodb.MongoClient; import com.mongodb.MongoClientURI; import com.mongodb.ReadPreference; @@ -74,6 +77,7 @@ import org.apache.jackrabbit.oak.plugins.document.locks.StripedNodeDocumentLocks import org.apache.jackrabbit.oak.plugins.document.util.Utils; import org.apache.jackrabbit.oak.stats.Clock; import org.apache.jackrabbit.oak.commons.PerfLogger; +import org.bson.BsonMaximumSizeExceededException; import org.bson.conversions.Bson; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -1009,6 +1013,15 @@ public class MongoDocumentStore implements DocumentStore { } } return oldDoc; + } catch (MongoWriteException e) { + WriteError werr = e.getError(); + LOG.error("Failed to update the document with Id={} with MongoWriteException message = '{}'.", + updateOp.getId(), werr.getMessage()); + throw handleException(e, collection, updateOp.getId()); + } catch (MongoCommandException e) { + LOG.error("Failed to update the document with Id={} with MongoCommandException message ='{}'. ", + updateOp.getId(), e.getMessage()); + throw handleException(e, collection, updateOp.getId()); } catch (Exception e) { throw handleException(e, collection, updateOp.getId()); } finally { @@ -1364,6 +1377,14 @@ public class MongoDocumentStore implements DocumentStore { } insertSuccess = true; return true; + } catch (BsonMaximumSizeExceededException e) { + for (T doc : docs) { + LOG.error("Failed to create one of the documents " + + "with BsonMaximumSizeExceededException message = '{}'. " + + "The document id={} has estimated size={} in VM.", + e.getMessage(), doc.getId(), doc.getMemory()); + } + return false; } catch (MongoException e) { LOG.warn("Encountered MongoException while inserting documents: {} - exception: {}", ids, e.getMessage()); diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDBExceptionTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDBExceptionTest.java index 73a1d9d49d..cfa0e7b6a5 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDBExceptionTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDBExceptionTest.java @@ -25,17 +25,23 @@ import org.apache.jackrabbit.oak.plugins.document.Path; import org.apache.jackrabbit.oak.plugins.document.Revision; import org.apache.jackrabbit.oak.plugins.document.UpdateOp; import org.apache.jackrabbit.oak.plugins.document.util.Utils; +import org.apache.jackrabbit.oak.commons.junit.LogCustomizer; import org.junit.After; import org.junit.Assume; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + import static java.util.Collections.singletonList; import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assert.assertFalse; public class MongoDBExceptionTest { @@ -132,8 +138,143 @@ public class MongoDBExceptionTest { } } + @Test + public void createOrUpdate16MBDoc() { + LogCustomizer customizer = LogCustomizer.forLogger(MongoDocumentStore.class.getName()).create(); + customizer.starting(); + String id = "/foo"; + UpdateOp updateOp = new UpdateOp(id, true); + updateOp = create16MBProp(updateOp); + exceptionMsg = "Document to upsert is larger than 16777216"; + try { + store.createOrUpdate(Collection.NODES, updateOp); + fail("DocumentStoreException expected"); + } catch (DocumentStoreException e) { + assertThat(e.getMessage(), containsString(exceptionMsg)); + String log = customizer.getLogs().toString(); + assertTrue("Message doesn't contain the id", log.contains(id)); + } + customizer.finished(); + } + + @Test + public void update16MBDoc() { + + String docName = "/foo"; + UpdateOp updateOp = new UpdateOp(docName, true); + updateOp = create1MBProp(updateOp); + store.createOrUpdate(Collection.NODES, updateOp); + updateOp = create16MBProp(updateOp); + exceptionMsg = "Resulting document after update is larger than 16777216"; + try { + store.createOrUpdate(Collection.NODES, updateOp); + fail("DocumentStoreException expected"); + } catch (DocumentStoreException e) { + assertThat(e.getMessage(), containsString(exceptionMsg)); + assertThat(e.getMessage(), containsString(docName)); + } + } + + @Test + public void multiCreateOrUpdate16MBDoc() { + + List<UpdateOp> updateOps = new ArrayList<>(); + LogCustomizer customizer = LogCustomizer.forLogger(MongoDocumentStore.class.getName()).create(); + customizer.starting(); + String id1 = "/test"; + String id2 = "/foo"; + + UpdateOp op = new UpdateOp(id1, true); + op = create1MBProp(op); + + store.createOrUpdate(Collection.NODES, op); + + UpdateOp op1 = new UpdateOp(id2, true); + op1 = create16MBProp(op1); + + // Updating both doc with 16MB + op = create16MBProp(op); + updateOps.add(op); + updateOps.add(op1); + exceptionMsg = "Resulting document after update is larger than 16777216"; + + try { + store.createOrUpdate(Collection.NODES, updateOps); + fail("DocumentStoreException expected"); + } catch (DocumentStoreException e) { + assertThat(e.getMessage(), containsString(exceptionMsg)); + String log = customizer.getLogs().toString(); + assertTrue("Message doesn't contain the id", log.contains(id1)); + } + customizer.finished(); + } + + @Test + public void create16MBDoc() { + + List<UpdateOp> updateOps = new ArrayList<>(); + LogCustomizer customizer = LogCustomizer.forLogger(MongoDocumentStore.class.getName()).create(); + customizer.starting(); + String id1 = "/test"; + String id2 = "/foo"; + UpdateOp op1 = new UpdateOp(id1, true); + op1 = create1MBProp(op1); + + UpdateOp op2 = new UpdateOp(id2, false); + op2 = create1MBProp(op2); + op2 = create16MBProp(op2); + + updateOps.add(op1); + updateOps.add(op2); + assertFalse(store.create(Collection.NODES, updateOps)); + String log = customizer.getLogs().toString(); + assertTrue("Message doesn't contain the id", log.contains(id2)); + } + + @Test + public void findAndUpdate16MBDoc() throws Exception { + String id = "/foo"; + UpdateOp op = new UpdateOp(id, true); + op = create1MBProp(op); + store.createOrUpdate(Collection.NODES, op); + op = create16MBProp(op); + exceptionMsg = "Resulting document after update is larger than 16777216"; + try { + store.findAndUpdate(Collection.NODES, op); + fail("DocumentStoreException expected"); + } catch (DocumentStoreException e) { + assertThat(e.getMessage(), containsString(exceptionMsg)); + assertThat(e.getMessage(), containsString(id)); + } + } + private void setExceptionMsg() { client.setExceptionBeforeUpdate(exceptionMsg); client.setExceptionBeforeQuery(exceptionMsg); } + + private UpdateOp create1MBProp(UpdateOp op) { + // create a 1 MB property + String content = create1MBContent(); + op.set("property0", content); + return op; + } + + private UpdateOp create16MBProp(UpdateOp op) { + // create a 1 MB property + String content = create1MBContent(); + + //create 16MB property + for (int i = 0; i < 16; i++) { + op.set("property"+ i, content); + } + return op; + } + + private String create1MBContent() { + char[] chars = new char[1024 * 1024]; + Arrays.fill(chars, '0'); + String content = new String(chars); + return content; + } }
