LogChopperTest.java
/*
* 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.zookeeper.test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.server.Request;
import org.apache.zookeeper.server.persistence.FileTxnLog;
import org.apache.zookeeper.server.persistence.TxnLog;
import org.apache.zookeeper.server.util.LogChopper;
import org.apache.zookeeper.txn.DeleteTxn;
import org.apache.zookeeper.txn.TxnHeader;
import org.junit.jupiter.api.Test;
class Pair<V1, V2> {
private V1 v1;
private V2 v2;
Pair(V1 v1, V2 v2) {
this.v1 = v1;
this.v2 = v2;
}
public V1 getFirst() {
return v1;
}
public V2 getSecond() {
return v2;
}
}
public class LogChopperTest extends ClientBase {
void rmr(File dir) throws IOException {
Files.walkFileTree(dir.toPath(), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes a) throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
}
Pair<Long, Long> getFirstLastZxid(File logFile) throws IOException {
File tmp = createTmpDir();
Files.copy(logFile.toPath(), new File(tmp, "log.0").toPath());
FileTxnLog txnLog = new FileTxnLog(tmp);
TxnLog.TxnIterator it = txnLog.read(0);
long firstZxid = it.getHeader().getZxid();
long lastZxid = firstZxid;
while (it.next()) {
lastZxid = it.getHeader().getZxid();
}
txnLog.close();
rmr(tmp);
return new Pair<>(firstZxid, lastZxid);
}
@Test
public void testChopper() throws IOException {
long clientId = 17;
int cxid = 77;
long zxid = 1000;
long time = 1;
int type = ZooDefs.OpCode.delete;
DeleteTxn txn = new DeleteTxn("/foo");
File tmpDir = createTmpDir();
FileTxnLog txnLog = new FileTxnLog(tmpDir);
for (int i = 0; i < 100; i++) {
TxnHeader hdr = new TxnHeader(clientId, cxid, ++zxid, ++time, type);
txnLog.append(new Request(0, 0, 0, hdr, txn, 0));
}
// append a txn with gap
TxnHeader hdr = new TxnHeader(clientId, cxid, zxid + 10, ++time, type);
txnLog.append(new Request(0, 0, 0, hdr, txn, 0));
txnLog.commit();
// now find the log we just created.
final File logFile = new File(tmpDir, "log." + Integer.toHexString(1001));
Pair<Long, Long> firstLast = getFirstLastZxid(logFile);
assertEquals(1001, (long) firstLast.getFirst());
assertEquals(1110, (long) firstLast.getSecond());
File choppedFile = new File(tmpDir, "chopped_failed");
assertFalse(LogChopper.chop(new FileInputStream(logFile), new FileOutputStream(choppedFile), 1107));
choppedFile = new File(tmpDir, "chopped");
assertTrue(LogChopper.chop(new FileInputStream(logFile), new FileOutputStream(choppedFile), 1017));
firstLast = getFirstLastZxid(choppedFile);
assertEquals(1001, (long) firstLast.getFirst());
assertEquals(1017, (long) firstLast.getSecond());
}
}