blob: aec32a54ddbcc56bf464ea994574e87275ac12a0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.oak.plugins.document;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Lists.partition;
import static org.apache.jackrabbit.oak.plugins.document.Collection.NODES;
/**
* Performs a rollback of a commit based on a list of changes. Changes are
* rolled back by applying the reverse operation to the document store.
*/
class Rollback {
static final Rollback FAILED = new Rollback(Revision.newRevision(0),
Collections.emptyList(), "", 1) {
@Override
void perform(@NotNull DocumentStore store) throws DocumentStoreException {
throw new DocumentStoreException("rollback failed");
}
};
static final Rollback NONE = new Rollback(Revision.newRevision(0),
Collections.emptyList(), "", 1) {
@Override
void perform(@NotNull DocumentStore store) throws DocumentStoreException {
}
};
private final Revision revision;
private final List<UpdateOp> changed;
private final String commitRootId;
private final int batchSize;
/**
* Creates a new rollback for the given commit revision.
*
* @param revision the commit revision.
* @param changed the changes to revert.
* @param commitRootId the id of the commit root document.
* @param batchSize the batch size for the rollback operations.
*/
Rollback(@NotNull Revision revision,
@NotNull List<UpdateOp> changed,
@NotNull String commitRootId,
int batchSize) {
this.revision = revision;
this.changed = checkNotNull(changed);
this.commitRootId = checkNotNull(commitRootId);
this.batchSize = batchSize;
}
/**
* Performs the rollback. If this operation fails with a
* {@link DocumentStoreException}, then only some of the rollback may have
* been performed.
*
* @param store the store where to apply the rollback.
* @throws DocumentStoreException if any of the operations fails.
*/
void perform(@NotNull DocumentStore store) throws DocumentStoreException {
checkNotNull(store);
List<UpdateOp> reverseOps = new ArrayList<>();
for (UpdateOp op : changed) {
UpdateOp reverse = op.getReverseOperation();
if (op.isNew()) {
NodeDocument.setDeletedOnce(reverse);
}
// do not create document if it doesn't exist
reverse.setNew(false);
reverseOps.add(reverse);
}
for (List<UpdateOp> ops : partition(reverseOps, batchSize)) {
store.createOrUpdate(NODES, ops);
}
removeCollisionMarker(store, commitRootId);
}
private void removeCollisionMarker(DocumentStore store, String id) {
UpdateOp removeCollision = new UpdateOp(id, false);
NodeDocument.removeCollision(removeCollision, revision);
store.findAndUpdate(NODES, removeCollision);
}
}