/** * 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 com.twitter.distributedlog; import com.twitter.distributedlog.LogSegmentMetadata.LogSegmentMetadataVersion; import com.twitter.util.Await; import com.twitter.util.FutureEventListener; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.concurrent.LinkedBlockingQueue; import static org.junit.Assert.*; /** * Test Cases related to sequence ids. */ public class TestSequenceID extends TestDistributedLogBase { static final Logger logger = LoggerFactory.getLogger(TestSequenceID.class); @Test(timeout = 60000) public void testCompleteV4LogSegmentAsV4() throws Exception { completeSingleInprogressSegment(LogSegmentMetadataVersion.VERSION_V4_ENVELOPED_ENTRIES.value, LogSegmentMetadataVersion.VERSION_V4_ENVELOPED_ENTRIES.value); } @Test(timeout = 60000) public void testCompleteV4LogSegmentAsV5() throws Exception { completeSingleInprogressSegment(LogSegmentMetadataVersion.VERSION_V4_ENVELOPED_ENTRIES.value, LogSegmentMetadataVersion.VERSION_V5_SEQUENCE_ID.value); } @Test(timeout = 60000) public void testCompleteV5LogSegmentAsV4() throws Exception { completeSingleInprogressSegment(LogSegmentMetadataVersion.VERSION_V5_SEQUENCE_ID.value, LogSegmentMetadataVersion.VERSION_V4_ENVELOPED_ENTRIES.value); } @Test(timeout = 60000) public void testCompleteV5LogSegmentAsV5() throws Exception { completeSingleInprogressSegment(LogSegmentMetadataVersion.VERSION_V5_SEQUENCE_ID.value, LogSegmentMetadataVersion.VERSION_V5_SEQUENCE_ID.value); } private void completeSingleInprogressSegment(int writeVersion, int completeVersion) throws Exception { DistributedLogConfiguration confLocal = new DistributedLogConfiguration(); confLocal.addConfiguration(conf); confLocal.setImmediateFlushEnabled(true); confLocal.setOutputBufferSize(0); confLocal.setDLLedgerMetadataLayoutVersion(writeVersion); String name = "distrlog-complete-single-inprogress-segment-versions-write-" + writeVersion + "-complete-" + completeVersion; BKDistributedLogManager dlm = (BKDistributedLogManager) createNewDLM(confLocal, name); BKAsyncLogWriter writer = dlm.startAsyncLogSegmentNonPartitioned(); Await.result(writer.write(DLMTestUtil.getLogRecordInstance(0L))); dlm.close(); DistributedLogConfiguration confLocal2 = new DistributedLogConfiguration(); confLocal2.addConfiguration(confLocal); confLocal2.setDLLedgerMetadataLayoutVersion(completeVersion); BKDistributedLogManager dlm2 = (BKDistributedLogManager) createNewDLM(confLocal2, name); dlm2.startAsyncLogSegmentNonPartitioned(); List<LogSegmentMetadata> segments = dlm2.getLogSegments(); assertEquals(1, segments.size()); if (LogSegmentMetadata.supportsSequenceId(writeVersion)) { if (LogSegmentMetadata.supportsSequenceId(completeVersion)) { // the inprogress log segment is written in v5 and complete log segment in v5, // then it support monotonic sequence id assertEquals(0L, segments.get(0).getStartSequenceId()); } else { // the inprogress log segment is written in v5 and complete log segment in v4, // then it doesn't support monotonic sequence id assertTrue(segments.get(0).getStartSequenceId() < 0); } } else { // if the inprogress log segment is created prior to v5, it won't support monotonic sequence id assertTrue(segments.get(0).getStartSequenceId() < 0); } dlm2.close(); } @Test(timeout = 60000) public void testSequenceID() throws Exception { DistributedLogConfiguration confLocalv4 = new DistributedLogConfiguration(); confLocalv4.addConfiguration(conf); confLocalv4.setImmediateFlushEnabled(true); confLocalv4.setOutputBufferSize(0); confLocalv4.setDLLedgerMetadataLayoutVersion(LogSegmentMetadataVersion.VERSION_V4_ENVELOPED_ENTRIES.value); String name = "distrlog-sequence-id"; BKDistributedLogManager readDLM = (BKDistributedLogManager) createNewDLM(conf, name); AsyncLogReader reader = null; final LinkedBlockingQueue<LogRecordWithDLSN> readRecords = new LinkedBlockingQueue<LogRecordWithDLSN>(); BKDistributedLogManager dlm = (BKDistributedLogManager) createNewDLM(confLocalv4, name); long txId = 0L; for (int i = 0; i < 3; i++) { BKAsyncLogWriter writer = dlm.startAsyncLogSegmentNonPartitioned(); for (int j = 0; j < 2; j++) { Await.result(writer.write(DLMTestUtil.getLogRecordInstance(txId++))); if (null == reader) { reader = readDLM.getAsyncLogReader(DLSN.InitialDLSN); final AsyncLogReader r = reader; reader.readNext().addEventListener(new FutureEventListener<LogRecordWithDLSN>() { @Override public void onSuccess(LogRecordWithDLSN record) { readRecords.add(record); r.readNext().addEventListener(this); } @Override public void onFailure(Throwable cause) { logger.error("Encountered exception on reading next : ", cause); } }); } } writer.closeAndComplete(); } BKAsyncLogWriter writer = dlm.startAsyncLogSegmentNonPartitioned(); Await.result(writer.write(DLMTestUtil.getLogRecordInstance(txId++))); List<LogSegmentMetadata> segments = dlm.getLogSegments(); assertEquals(4, segments.size()); for (int i = 0; i < 3; i++) { assertFalse(segments.get(i).isInProgress()); assertTrue(segments.get(i).getStartSequenceId() < 0); } assertTrue(segments.get(3).isInProgress()); assertTrue(segments.get(3).getStartSequenceId() < 0); dlm.close(); // simulate upgrading from v4 -> v5 DistributedLogConfiguration confLocalv5 = new DistributedLogConfiguration(); confLocalv5.addConfiguration(conf); confLocalv5.setImmediateFlushEnabled(true); confLocalv5.setOutputBufferSize(0); confLocalv5.setDLLedgerMetadataLayoutVersion(LogSegmentMetadataVersion.VERSION_V5_SEQUENCE_ID.value); BKDistributedLogManager dlmv5 = (BKDistributedLogManager) createNewDLM(confLocalv5, name); for (int i = 0; i < 3; i++) { BKAsyncLogWriter writerv5 = dlmv5.startAsyncLogSegmentNonPartitioned(); for (int j = 0; j < 2; j++) { Await.result(writerv5.write(DLMTestUtil.getLogRecordInstance(txId++))); } writerv5.closeAndComplete(); } BKAsyncLogWriter writerv5 = dlmv5.startAsyncLogSegmentNonPartitioned(); Await.result(writerv5.write(DLMTestUtil.getLogRecordInstance(txId++))); List<LogSegmentMetadata> segmentsv5 = dlmv5.getLogSegments(); assertEquals(8, segmentsv5.size()); assertFalse(segmentsv5.get(3).isInProgress()); assertTrue(segmentsv5.get(3).getStartSequenceId() < 0); long startSequenceId = 0L; for (int i = 4; i < 7; i++) { assertFalse(segmentsv5.get(i).isInProgress()); assertEquals(startSequenceId, segmentsv5.get(i).getStartSequenceId()); startSequenceId += 2L; } assertTrue(segmentsv5.get(7).isInProgress()); assertEquals(startSequenceId, segmentsv5.get(7).getStartSequenceId()); dlmv5.close(); // rollback from v5 to v4 BKDistributedLogManager dlmv4 = (BKDistributedLogManager) createNewDLM(confLocalv4, name); for (int i = 0; i < 3; i++) { BKAsyncLogWriter writerv4 = dlmv4.startAsyncLogSegmentNonPartitioned(); for (int j = 0; j < 2; j++) { Await.result(writerv4.write(DLMTestUtil.getLogRecordInstance(txId++))); } writerv4.closeAndComplete(); } List<LogSegmentMetadata> segmentsv4 = dlmv4.getLogSegments(); assertEquals(11, segmentsv4.size()); for(int i = 7; i < 11; i++) { assertFalse(segmentsv4.get(i).isInProgress()); assertTrue(segmentsv4.get(i).getStartSequenceId() < 0); } dlmv4.close(); // wait until readers read all records while (readRecords.size() < txId) { Thread.sleep(100); } assertEquals(txId, readRecords.size()); long sequenceId = Long.MIN_VALUE; for (LogRecordWithDLSN record : readRecords) { if (record.getDlsn().getLogSegmentSequenceNo() <= 4) { assertTrue(record.getSequenceId() < 0); assertTrue(record.getSequenceId() > sequenceId); sequenceId = record.getSequenceId(); } else if (record.getDlsn().getLogSegmentSequenceNo() <= 7) { if (sequenceId < 0L) { sequenceId = 0L; } assertEquals(sequenceId, record.getSequenceId()); ++sequenceId; } else if (record.getDlsn().getLogSegmentSequenceNo() >= 9) { if (sequenceId > 0) { sequenceId = Long.MIN_VALUE; } assertTrue(record.getSequenceId() < 0); assertTrue(record.getSequenceId() > sequenceId); sequenceId = record.getSequenceId(); } } readDLM.close(); } }