This is an automated email from the ASF dual-hosted git repository.
reschke pushed a commit to branch 1.22
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
The following commit(s) were added to refs/heads/1.22 by this push:
new de0eaad451 OAK-10643: MongoDocumentStore: improve diagnostics for too
large docs (backport to 1.22) (#2755)
de0eaad451 is described below
commit de0eaad451d948d98bb4456385bd3f5800737d8e
Author: Julian Reschke <[email protected]>
AuthorDate: Fri Feb 20 13:41:29 2026 +0100
OAK-10643: MongoDocumentStore: improve diagnostics for too large docs
(backport to 1.22) (#2755)
---
.../plugins/document/mongo/MongoDocumentStore.java | 23 ++++++-
.../oak/plugins/document/util/Utils.java | 72 ++++++++++++++++++++++
2 files changed, 93 insertions(+), 2 deletions(-)
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 4b42d3f53b..fe2ca3d104 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
@@ -925,6 +925,7 @@ public class MongoDocumentStore implements DocumentStore {
}
final Stopwatch watch = startWatch();
boolean newEntry = false;
+
try {
// get modCount of cached document
Long modCount = null;
@@ -992,6 +993,7 @@ public class MongoDocumentStore implements DocumentStore {
if (checkConditions && oldNode == null) {
return null;
}
+
T oldDoc = convertFromDBObject(collection, oldNode);
if (oldDoc != null) {
if (collection == Collection.NODES) {
@@ -1015,8 +1017,8 @@ 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());
+ LOG.error("Failed to update the document with Id={} with
MongoWriteException message = '{}'. Document statistics: {}.",
+ updateOp.getId(), werr.getMessage(),
produceDiagnostics(collection, updateOp.getId()), e);
throw handleException(e, collection, updateOp.getId());
} catch (MongoCommandException e) {
LOG.error("Failed to update the document with Id={} with
MongoCommandException message ='{}'. ",
@@ -1044,6 +1046,23 @@ public class MongoDocumentStore implements DocumentStore
{
return doc;
}
+ private <T extends Document> String produceDiagnostics(Collection<T> col,
String id) {
+ StringBuilder t = new StringBuilder();
+
+ try {
+ T doc = find(col, id);
+ if (doc != null) {
+ t.append("_id: " + doc.getId() + ", _modCount: " +
doc.getModCount() + ", memory: " + doc.getMemory());
+ t.append("; Contents: ");
+ t.append(Utils.mapEntryDiagnostics(doc.entrySet()));
+ }
+ } catch (Throwable thisIsBestEffort) {
+ t.append(thisIsBestEffort.getMessage());
+ }
+
+ return t.toString();
+ }
+
/**
* Try to apply all the {@link UpdateOp}s with at least MongoDB requests as
* possible. The return value is the list of the old documents (before
diff --git
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/Utils.java
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/Utils.java
index 57a0b04a9b..8c2ce28e68 100644
---
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/Utils.java
+++
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/Utils.java
@@ -23,12 +23,14 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Timestamp;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
@@ -201,6 +203,76 @@ public class Utils {
return (int) size;
}
+ private static class PropertyStats {
+ public int count;
+ public int size;
+ }
+
+ public static String mapEntryDiagnostics(@NotNull Set<Entry<String,
Object>> entries) {
+ StringBuilder t = new StringBuilder();
+ Map<String, PropertyStats> stats = new TreeMap<>();
+
+ for (Map.Entry<String, Object> member : entries) {
+ String key = member.getKey();
+
+ PropertyStats stat = stats.get(key);
+ if (stat == null) {
+ stat = new PropertyStats();
+ }
+
+ Object o = member.getValue();
+ if (o instanceof String) {
+ stat.size += StringUtils.estimateMemoryUsage((String) o);
+ stat.count += 1;
+ } else if (o instanceof Long) {
+ stat.size += 16;
+ stat.count += 1;
+ } else if (o instanceof Boolean) {
+ stat.size += 8;
+ stat.count += 1;
+ } else if (o instanceof Integer) {
+ stat.size += 8;
+ stat.count += 1;
+ } else if (o instanceof Map) {
+ @SuppressWarnings("unchecked")
+ Map<Object, Object> x = (Map<Object, Object>)o;
+ stat.size += 8 + Utils.estimateMemoryUsage(x);
+ stat.count += x.size();
+ } else if (o == null) {
+ // zero
+ } else {
+ throw new IllegalArgumentException("Can't estimate memory
usage of " + o);
+ }
+
+ stats.put(key, stat);
+ }
+
+ List<Map.Entry<String, PropertyStats>> sorted = new
ArrayList<Entry<String, PropertyStats>>(stats.entrySet());
+
+ // sort by estimated entry size, highest first
+ Collections.sort(sorted, new Comparator<Map.Entry<String,
PropertyStats>>() {
+ @Override
+ public int compare(Entry<String, PropertyStats> o1, Entry<String,
PropertyStats> o2) {
+ return o2.getValue().size - o1.getValue().size;
+ }
+ });
+
+ String sep = "";
+ for (Map.Entry<String, PropertyStats> member : sorted) {
+ String name = member.getKey();
+ PropertyStats stat = member.getValue();
+ t.append("'" + sep + name + "': ");
+ sep = ", ";
+ if (stat.count <= 1) {
+ t.append(stat.size + " bytes");
+ } else {
+ t.append(stat.size + " bytes in " + stat.count + " entries ("
+ stat.size / stat.count + " avg)");
+ }
+ }
+
+ return t.toString();
+ }
+
public static String escapePropertyName(String propertyName) {
int len = propertyName.length();
if (len == 0) {