use of org.apache.jackrabbit.oak.plugins.document.UpdateOp.Operation in project jackrabbit-oak by apache.
the class RDBDocumentSerializer method asString.
/**
* Serializes the changes in the {@link UpdateOp} into a JSON array; each
* entry is another JSON array holding operation, key, revision, and value.
*/
public String asString(UpdateOp update) {
StringBuilder sb = new StringBuilder("[");
boolean needComma = false;
for (Map.Entry<Key, Operation> change : update.getChanges().entrySet()) {
Operation op = change.getValue();
Key key = change.getKey();
// exclude properties that are serialized into special columns
if (columnProperties.contains(key.getName()) && null == key.getRevision())
continue;
if (needComma) {
sb.append(",");
}
sb.append("[");
if (op.type == UpdateOp.Operation.Type.INCREMENT) {
sb.append("\"+\",");
} else if (op.type == UpdateOp.Operation.Type.SET || op.type == UpdateOp.Operation.Type.SET_MAP_ENTRY) {
sb.append("\"=\",");
} else if (op.type == UpdateOp.Operation.Type.MAX) {
sb.append("\"M\",");
} else if (op.type == UpdateOp.Operation.Type.REMOVE || op.type == UpdateOp.Operation.Type.REMOVE_MAP_ENTRY) {
sb.append("\"*\",");
} else {
throw new DocumentStoreException("Can't serialize " + update.toString() + " for JSON append");
}
appendJsonString(sb, key.getName());
sb.append(",");
Revision rev = key.getRevision();
if (rev != null) {
appendJsonString(sb, rev.toString());
sb.append(",");
}
appendJsonValue(sb, op.value);
sb.append("]");
needComma = true;
}
return sb.append("]").toString();
}
use of org.apache.jackrabbit.oak.plugins.document.UpdateOp.Operation in project jackrabbit-oak by apache.
the class RDBDocumentStore method updateDocument.
private <T extends Document> boolean updateDocument(@Nonnull Collection<T> collection, @Nonnull T document, @Nonnull UpdateOp update, Long oldmodcount) {
Connection connection = null;
RDBTableMetaData tmd = getTable(collection);
String data = null;
try {
connection = this.ch.getRWConnection();
Number hasBinary = (Number) document.get(NodeDocument.HAS_BINARY_FLAG);
Boolean deletedOnce = (Boolean) document.get(NodeDocument.DELETED_ONCE);
Long modcount = (Long) document.get(MODCOUNT);
Long cmodcount = (Long) document.get(COLLISIONSMODCOUNT);
boolean success = false;
boolean shouldRetry = true;
// every 16th update is a full rewrite
if (isAppendableUpdate(update, false) && modcount % 16 != 0) {
String appendData = ser.asString(update);
if (appendData.length() < tmd.getDataLimitInOctets() / CHAR2OCTETRATIO) {
try {
Operation modOperation = update.getChanges().get(MODIFIEDKEY);
long modified = getModifiedFromOperation(modOperation);
boolean modifiedIsConditional = modOperation == null || modOperation.type != UpdateOp.Operation.Type.SET;
success = db.appendingUpdate(connection, tmd, document.getId(), modified, modifiedIsConditional, hasBinary, deletedOnce, modcount, cmodcount, oldmodcount, appendData);
// if we get here, a retry is not going to help (the SQL
// operation succeeded but simply did not select a row
// that could be updated, likely because of the check on
// MODCOUNT
shouldRetry = false;
connection.commit();
} catch (SQLException ex) {
continueIfStringOverflow(ex);
this.ch.rollbackConnection(connection);
success = false;
}
}
}
if (!success && shouldRetry) {
data = ser.asString(document);
Object m = document.get(MODIFIED);
long modified = (m instanceof Long) ? ((Long) m).longValue() : 0;
success = db.update(connection, tmd, document.getId(), modified, hasBinary, deletedOnce, modcount, cmodcount, oldmodcount, data);
connection.commit();
}
return success;
} catch (SQLException ex) {
this.ch.rollbackConnection(connection);
String addDiags = "";
if (RDBJDBCTools.matchesSQLState(ex, "22", "72")) {
byte[] bytes = asBytes(data);
addDiags = String.format(" (DATA size in Java characters: %d, in octets: %d, computed character limit: %d)", data.length(), bytes.length, tmd.getDataLimitInOctets() / CHAR2OCTETRATIO);
}
String message = String.format("Update for %s failed%s", document.getId(), addDiags);
LOG.debug(message, ex);
throw handleException(message, ex, collection, document.getId());
} finally {
this.ch.closeConnection(connection);
}
}
use of org.apache.jackrabbit.oak.plugins.document.UpdateOp.Operation in project jackrabbit-oak by apache.
the class MongoDocumentStore method createUpdate.
/**
* Creates a MongoDB update object from the given UpdateOp.
*
* @param updateOp the update op.
* @param includeId whether to include the SET id operation
* @return the DBObject.
*/
@Nonnull
private static DBObject createUpdate(UpdateOp updateOp, boolean includeId) {
BasicDBObject setUpdates = new BasicDBObject();
BasicDBObject maxUpdates = new BasicDBObject();
BasicDBObject incUpdates = new BasicDBObject();
BasicDBObject unsetUpdates = new BasicDBObject();
// always increment modCount
updateOp.increment(Document.MOD_COUNT, 1);
if (includeId) {
setUpdates.append(Document.ID, updateOp.getId());
}
// other updates
for (Entry<Key, Operation> entry : updateOp.getChanges().entrySet()) {
Key k = entry.getKey();
Operation op = entry.getValue();
switch(op.type) {
case SET:
case SET_MAP_ENTRY:
{
setUpdates.append(k.toString(), op.value);
break;
}
case MAX:
{
maxUpdates.append(k.toString(), op.value);
break;
}
case INCREMENT:
{
incUpdates.append(k.toString(), op.value);
break;
}
case REMOVE:
case REMOVE_MAP_ENTRY:
{
unsetUpdates.append(k.toString(), "1");
break;
}
}
}
BasicDBObject update = new BasicDBObject();
if (!setUpdates.isEmpty()) {
update.append("$set", setUpdates);
}
if (!maxUpdates.isEmpty()) {
update.append("$max", maxUpdates);
}
if (!incUpdates.isEmpty()) {
update.append("$inc", incUpdates);
}
if (!unsetUpdates.isEmpty()) {
update.append("$unset", unsetUpdates);
}
return update;
}
use of org.apache.jackrabbit.oak.plugins.document.UpdateOp.Operation in project jackrabbit-oak by apache.
the class MongoDocumentStore method create.
@Override
public <T extends Document> boolean create(Collection<T> collection, List<UpdateOp> updateOps) {
log("create", updateOps);
List<T> docs = new ArrayList<T>();
DBObject[] inserts = new DBObject[updateOps.size()];
List<String> ids = Lists.newArrayListWithCapacity(updateOps.size());
for (int i = 0; i < updateOps.size(); i++) {
inserts[i] = new BasicDBObject();
UpdateOp update = updateOps.get(i);
inserts[i].put(Document.ID, update.getId());
UpdateUtils.assertUnconditional(update);
T target = collection.newDocument(this);
UpdateUtils.applyChanges(target, update);
docs.add(target);
ids.add(updateOps.get(i).getId());
for (Entry<Key, Operation> entry : update.getChanges().entrySet()) {
Key k = entry.getKey();
Operation op = entry.getValue();
switch(op.type) {
case SET:
case MAX:
case INCREMENT:
{
inserts[i].put(k.toString(), op.value);
break;
}
case SET_MAP_ENTRY:
{
Revision r = k.getRevision();
if (r == null) {
throw new IllegalStateException("SET_MAP_ENTRY must not have null revision");
}
DBObject value = (DBObject) inserts[i].get(k.getName());
if (value == null) {
value = new RevisionEntry(r, op.value);
inserts[i].put(k.getName(), value);
} else if (value.keySet().size() == 1) {
String key = value.keySet().iterator().next();
Object val = value.get(key);
value = new BasicDBObject(key, val);
value.put(r.toString(), op.value);
inserts[i].put(k.getName(), value);
} else {
value.put(r.toString(), op.value);
}
break;
}
case REMOVE:
case REMOVE_MAP_ENTRY:
// nothing to do for new entries
break;
}
}
if (!inserts[i].containsField(Document.MOD_COUNT)) {
inserts[i].put(Document.MOD_COUNT, 1L);
target.put(Document.MOD_COUNT, 1L);
}
}
DBCollection dbCollection = getDBCollection(collection);
final Stopwatch watch = startWatch();
boolean insertSuccess = false;
try {
try {
dbCollection.insert(inserts);
if (collection == Collection.NODES) {
for (T doc : docs) {
nodesCache.putIfAbsent((NodeDocument) doc);
updateLocalChanges((NodeDocument) doc);
}
}
insertSuccess = true;
return true;
} catch (MongoException e) {
return false;
}
} finally {
stats.doneCreate(watch.elapsed(TimeUnit.NANOSECONDS), collection, ids, insertSuccess);
}
}
use of org.apache.jackrabbit.oak.plugins.document.UpdateOp.Operation in project jackrabbit-oak by apache.
the class CommitRootUpdateTest method exceptionOnUpdate.
@Test
public void exceptionOnUpdate() throws Exception {
final AtomicBoolean throwAfterUpdate = new AtomicBoolean(false);
MemoryDocumentStore store = new MemoryDocumentStore() {
@Override
public <T extends Document> T findAndUpdate(Collection<T> collection, UpdateOp update) {
T doc = super.findAndUpdate(collection, update);
if (isFinalCommitRootUpdate(update) && throwAfterUpdate.compareAndSet(true, false)) {
throw new RuntimeException("communication failure");
}
return doc;
}
private boolean isFinalCommitRootUpdate(UpdateOp update) {
boolean finalUpdate = true;
for (Map.Entry<Key, Operation> op : update.getChanges().entrySet()) {
String name = op.getKey().getName();
if (NodeDocument.isRevisionsEntry(name) || NodeDocument.MODIFIED_IN_SECS.equals(name)) {
continue;
}
finalUpdate = false;
break;
}
return finalUpdate;
}
};
DocumentNodeStore ns = builderProvider.newBuilder().setDocumentStore(store).setAsyncDelay(0).getNodeStore();
NodeBuilder b = ns.getRoot().builder();
b.child("foo");
b.child("bar");
merge(ns, b);
throwAfterUpdate.set(true);
boolean success = false;
Commit c = ns.newCommit(ns.getHeadRevision(), null);
try {
c.addNode(new DocumentNodeState(ns, "/foo/node", c.getBaseRevision()));
c.addNode(new DocumentNodeState(ns, "/bar/node", c.getBaseRevision()));
c.apply();
success = true;
} finally {
if (success) {
ns.done(c, false, CommitInfo.EMPTY);
} else {
ns.canceled(c);
}
}
NodeState root = ns.getRoot();
assertTrue(root.getChildNode("foo").getChildNode("node").exists());
assertTrue(root.getChildNode("bar").getChildNode("node").exists());
assertFalse(throwAfterUpdate.get());
}
Aggregations