/* * 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.lucene.index; import java.io.IOException; import java.util.BitSet; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.lucene.analysis.MockAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.store.Directory; import org.apache.lucene.util.Bits; import org.apache.lucene.util.LuceneTestCase; /** * Test class to illustrate using IndexDeletionPolicy to provide multi-level rollback capability. * This test case creates an index of records 1 to 100, introducing a commit point every 10 records. * * A "keep all" deletion policy is used to ensure we keep all commit points for testing purposes */ public class TestTransactionRollback extends LuceneTestCase { private static final String FIELD_RECORD_ID = "record_id"; private Directory dir; //Rolls back index to a chosen ID private void rollBackLast(int id) throws Exception { // System.out.println("Attempting to rollback to "+id); String ids="-"+id; IndexCommit last=null; Collection<IndexCommit> commits = DirectoryReader.listCommits(dir); for (Iterator<IndexCommit> iterator = commits.iterator(); iterator.hasNext();) { IndexCommit commit = iterator.next(); Map<String,String> ud=commit.getUserData(); if (ud.size() > 0) { if (ud.get("index").endsWith(ids)) { last = commit; } } } if (last==null) { throw new RuntimeException("Couldn't find commit point "+id); } IndexWriter w = new IndexWriter(dir, newIndexWriterConfig(new MockAnalyzer(random())) .setIndexDeletionPolicy(new RollbackDeletionPolicy(id)) .setIndexCommit(last)); Map<String,String> data = new HashMap<>(); data.put("index", "Rolled back to 1-"+id); w.setLiveCommitData(data.entrySet()); w.close(); } public void testRepeatedRollBacks() throws Exception { int expectedLastRecordId = 100; while (expectedLastRecordId>10) { expectedLastRecordId -= 10; rollBackLast(expectedLastRecordId); BitSet expecteds = new BitSet(100); expecteds.set(1, (expectedLastRecordId+1), true); checkExpecteds(expecteds); } } private void checkExpecteds(BitSet expecteds) throws Exception { IndexReader r = DirectoryReader.open(dir); //Perhaps not the most efficient approach but meets our //needs here. final Bits liveDocs = MultiFields.getLiveDocs(r); for (int i = 0; i < r.maxDoc(); i++) { if (liveDocs == null || liveDocs.get(i)) { String sval=r.document(i).get(FIELD_RECORD_ID); if(sval!=null) { int val=Integer.parseInt(sval); assertTrue("Did not expect document #"+val, expecteds.get(val)); expecteds.set(val,false); } } } r.close(); assertEquals("Should have 0 docs remaining ", 0 ,expecteds.cardinality()); } /* private void showAvailableCommitPoints() throws Exception { Collection commits = DirectoryReader.listCommits(dir); for (Iterator iterator = commits.iterator(); iterator.hasNext();) { IndexCommit comm = (IndexCommit) iterator.next(); System.out.print("\t Available commit point:["+comm.getUserData()+"] files="); Collection files = comm.getFileNames(); for (Iterator iterator2 = files.iterator(); iterator2.hasNext();) { String filename = (String) iterator2.next(); System.out.print(filename+", "); } System.out.println(); } } */ @Override public void setUp() throws Exception { super.setUp(); dir = newDirectory(); //Build index, of records 1 to 100, committing after each batch of 10 IndexDeletionPolicy sdp=new KeepAllDeletionPolicy(); IndexWriter w=new IndexWriter(dir, newIndexWriterConfig(new MockAnalyzer(random())) .setIndexDeletionPolicy(sdp)); for(int currentRecordId=1;currentRecordId<=100;currentRecordId++) { Document doc=new Document(); doc.add(newTextField(FIELD_RECORD_ID, ""+currentRecordId, Field.Store.YES)); w.addDocument(doc); if (currentRecordId%10 == 0) { Map<String,String> data = new HashMap<>(); data.put("index", "records 1-"+currentRecordId); w.setLiveCommitData(data.entrySet()); w.commit(); } } w.close(); } @Override public void tearDown() throws Exception { dir.close(); super.tearDown(); } // Rolls back to previous commit point static class RollbackDeletionPolicy extends IndexDeletionPolicy { private int rollbackPoint; public RollbackDeletionPolicy(int rollbackPoint) { this.rollbackPoint = rollbackPoint; } @Override public void onCommit(List<? extends IndexCommit> commits) throws IOException { } @Override public void onInit(List<? extends IndexCommit> commits) throws IOException { for (final IndexCommit commit : commits) { Map<String,String> userData=commit.getUserData(); if (userData.size() > 0) { // Label for a commit point is "Records 1-30" // This code reads the last id ("30" in this example) and deletes it // if it is after the desired rollback point String x = userData.get("index"); String lastVal = x.substring(x.lastIndexOf("-")+1); int last = Integer.parseInt(lastVal); if (last>rollbackPoint) { /* System.out.print("\tRolling back commit point:" + " UserData="+commit.getUserData() +") ("+(commits.size()-1)+" commit points left) files="); Collection files = commit.getFileNames(); for (Iterator iterator2 = files.iterator(); iterator2.hasNext();) { System.out.print(" "+iterator2.next()); } System.out.println(); */ commit.delete(); } } } } } static class DeleteLastCommitPolicy extends IndexDeletionPolicy { @Override public void onCommit(List<? extends IndexCommit> commits) throws IOException {} @Override public void onInit(List<? extends IndexCommit> commits) throws IOException { commits.get(commits.size()-1).delete(); } } public void testRollbackDeletionPolicy() throws Exception { for(int i=0;i<2;i++) { // Unless you specify a prior commit point, rollback // should not work: new IndexWriter(dir, newIndexWriterConfig(new MockAnalyzer(random())) .setIndexDeletionPolicy(new DeleteLastCommitPolicy())).close(); IndexReader r = DirectoryReader.open(dir); assertEquals(100, r.numDocs()); r.close(); } } // Keeps all commit points (used to build index) static class KeepAllDeletionPolicy extends IndexDeletionPolicy { @Override public void onCommit(List<? extends IndexCommit> commits) throws IOException {} @Override public void onInit(List<? extends IndexCommit> commits) throws IOException {} } }