pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / backwards / src / test / org / apache / lucene / index / TestSnapshotDeletionPolicy.java
1 package org.apache.lucene.index;
2
3 /**
4  * Licensed to the Apache Software Foundation (ASF) under one or more
5  * contributor license agreements.  See the NOTICE file distributed with
6  * this work for additional information regarding copyright ownership.
7  * The ASF licenses this file to You under the Apache License, Version 2.0
8  * (the "License"); you may not use this file except in compliance with
9  * the License.  You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 import java.util.Collection;
21 import java.util.Map;
22 import java.util.Random;
23 import java.io.IOException;
24
25 import org.apache.lucene.document.Document;
26 import org.apache.lucene.document.Field;
27 import org.apache.lucene.store.Directory;
28 import org.apache.lucene.store.IndexInput;
29 import org.apache.lucene.analysis.KeywordAnalyzer;
30 import org.apache.lucene.analysis.MockAnalyzer;
31 import org.apache.lucene.analysis.standard.StandardAnalyzer;
32 import org.apache.lucene.index.IndexCommit;
33 import org.apache.lucene.index.IndexWriterConfig;
34 import org.apache.lucene.index.KeepOnlyLastCommitDeletionPolicy;
35 import org.apache.lucene.index.IndexWriter;
36 import org.apache.lucene.index.SnapshotDeletionPolicy;
37 import org.apache.lucene.util.LuceneTestCase;
38 import org.apache.lucene.util.ThreadInterruptedException;
39 import org.junit.Test;
40
41 //
42 // This was developed for Lucene In Action,
43 // http://lucenebook.com
44 //
45
46 public class TestSnapshotDeletionPolicy extends LuceneTestCase {
47   public static final String INDEX_PATH = "test.snapshots";
48   
49   protected IndexWriterConfig getConfig(Random random, IndexDeletionPolicy dp) {
50     IndexWriterConfig conf = newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random));
51     if (dp != null) {
52       conf.setIndexDeletionPolicy(dp);
53     }
54     return conf;
55   }
56
57   protected void checkSnapshotExists(Directory dir, IndexCommit c) throws Exception {
58     String segFileName = c.getSegmentsFileName();
59     assertTrue("segments file not found in directory: " + segFileName, dir.fileExists(segFileName));
60   }
61
62   protected void checkMaxDoc(IndexCommit commit, int expectedMaxDoc) throws Exception {
63     IndexReader reader = IndexReader.open(commit, true);
64     try {
65       assertEquals(expectedMaxDoc, reader.maxDoc());
66     } finally {
67       reader.close();
68     }
69   }
70
71   protected void prepareIndexAndSnapshots(SnapshotDeletionPolicy sdp,
72       IndexWriter writer, int numSnapshots, String snapshotPrefix)
73       throws RuntimeException, IOException {
74     for (int i = 0; i < numSnapshots; i++) {
75       // create dummy document to trigger commit.
76       writer.addDocument(new Document());
77       writer.commit();
78       sdp.snapshot(snapshotPrefix + i);
79     }
80   }
81
82   protected SnapshotDeletionPolicy getDeletionPolicy() throws IOException {
83     return getDeletionPolicy(null);
84   }
85
86   protected SnapshotDeletionPolicy getDeletionPolicy(Map<String, String> snapshots) throws IOException {
87     return new SnapshotDeletionPolicy(new KeepOnlyLastCommitDeletionPolicy(), snapshots);
88   }
89
90   protected void assertSnapshotExists(Directory dir, SnapshotDeletionPolicy sdp, int numSnapshots) throws Exception {
91     for (int i = 0; i < numSnapshots; i++) {
92       IndexCommit snapshot = sdp.getSnapshot("snapshot" + i);
93       checkMaxDoc(snapshot, i + 1);
94       checkSnapshotExists(dir, snapshot);
95     }
96   }
97   
98   @Test
99   public void testSnapshotDeletionPolicy() throws Exception {
100     Directory fsDir = newDirectory();
101     runTest(random, fsDir);
102     fsDir.close();
103   }
104
105   private void runTest(Random random, Directory dir) throws Exception {
106     // Run for ~1 seconds
107     final long stopTime = System.currentTimeMillis() + 1000;
108
109     SnapshotDeletionPolicy dp = getDeletionPolicy();
110     final IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(
111         TEST_VERSION_CURRENT, new MockAnalyzer(random)).setIndexDeletionPolicy(dp)
112         .setMaxBufferedDocs(2));
113     writer.commit();
114     
115     final Thread t = new Thread() {
116         @Override
117         public void run() {
118           Document doc = new Document();
119           doc.add(newField("content", "aaa", Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS));
120           do {
121             for(int i=0;i<27;i++) {
122               try {
123                 writer.addDocument(doc);
124               } catch (Throwable t) {
125                 t.printStackTrace(System.out);
126                 fail("addDocument failed");
127               }
128               if (i%2 == 0) {
129                 try {
130                   writer.commit();
131                 } catch (Exception e) {
132                   throw new RuntimeException(e);
133                 }
134               }
135             }
136             try {
137               Thread.sleep(1);
138             } catch (InterruptedException ie) {
139               throw new ThreadInterruptedException(ie);
140             }
141           } while(System.currentTimeMillis() < stopTime);
142         }
143       };
144
145     t.start();
146
147     // While the above indexing thread is running, take many
148     // backups:
149     do {
150       backupIndex(dir, dp);
151       Thread.sleep(20);
152     } while(t.isAlive());
153
154     t.join();
155
156     // Add one more document to force writer to commit a
157     // final segment, so deletion policy has a chance to
158     // delete again:
159     Document doc = new Document();
160     doc.add(newField("content", "aaa", Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS));
161     writer.addDocument(doc);
162
163     // Make sure we don't have any leftover files in the
164     // directory:
165     writer.close();
166     TestIndexWriter.assertNoUnreferencedFiles(dir, "some files were not deleted but should have been");
167   }
168
169   /**
170    * Example showing how to use the SnapshotDeletionPolicy to take a backup.
171    * This method does not really do a backup; instead, it reads every byte of
172    * every file just to test that the files indeed exist and are readable even
173    * while the index is changing.
174    */
175   public void backupIndex(Directory dir, SnapshotDeletionPolicy dp) throws Exception {
176     // To backup an index we first take a snapshot:
177     try {
178       copyFiles(dir,  dp.snapshot("id"));
179     } finally {
180       // Make sure to release the snapshot, otherwise these
181       // files will never be deleted during this IndexWriter
182       // session:
183       dp.release("id");
184     }
185   }
186
187   private void copyFiles(Directory dir, IndexCommit cp) throws Exception {
188
189     // While we hold the snapshot, and nomatter how long
190     // we take to do the backup, the IndexWriter will
191     // never delete the files in the snapshot:
192     Collection<String> files = cp.getFileNames();
193     for (final String fileName : files) { 
194       // NOTE: in a real backup you would not use
195       // readFile; you would need to use something else
196       // that copies the file to a backup location.  This
197       // could even be a spawned shell process (eg "tar",
198       // "zip") that takes the list of files and builds a
199       // backup.
200       readFile(dir, fileName);
201     }
202   }
203
204   byte[] buffer = new byte[4096];
205
206   private void readFile(Directory dir, String name) throws Exception {
207     IndexInput input = dir.openInput(name);
208     try {
209       long size = dir.fileLength(name);
210       long bytesLeft = size;
211       while (bytesLeft > 0) {
212         final int numToRead;
213         if (bytesLeft < buffer.length)
214           numToRead = (int) bytesLeft;
215         else
216           numToRead = buffer.length;
217         input.readBytes(buffer, 0, numToRead, false);
218         bytesLeft -= numToRead;
219       }
220       // Don't do this in your real backups!  This is just
221       // to force a backup to take a somewhat long time, to
222       // make sure we are exercising the fact that the
223       // IndexWriter should not delete this file even when I
224       // take my time reading it.
225       Thread.sleep(1);
226     } finally {
227       input.close();
228     }
229   }
230
231   
232   @Test
233   public void testBasicSnapshots() throws Exception {
234     int numSnapshots = 3;
235     SnapshotDeletionPolicy sdp = getDeletionPolicy();
236     
237     // Create 3 snapshots: snapshot0, snapshot1, snapshot2
238     Directory dir = newDirectory();
239     IndexWriter writer = new IndexWriter(dir, getConfig(random, sdp));
240     prepareIndexAndSnapshots(sdp, writer, numSnapshots, "snapshot");
241     writer.close();
242     
243     assertSnapshotExists(dir, sdp, numSnapshots);
244
245     // open a reader on a snapshot - should succeed.
246     IndexReader.open(sdp.getSnapshot("snapshot0"), true).close();
247
248     // open a new IndexWriter w/ no snapshots to keep and assert that all snapshots are gone.
249     sdp = getDeletionPolicy();
250     writer = new IndexWriter(dir, getConfig(random, sdp));
251     writer.deleteUnusedFiles();
252     writer.close();
253     assertEquals("no snapshots should exist", 1, IndexReader.listCommits(dir).size());
254     
255     for (int i = 0; i < numSnapshots; i++) {
256       try {
257         sdp.getSnapshot("snapshot" + i);
258         fail("snapshot shouldn't have existed, but did: snapshot" + i);
259       } catch (IllegalStateException e) {
260         // expected - snapshot should not exist
261       }
262     }
263     dir.close();
264   }
265
266   @Test
267   public void testMultiThreadedSnapshotting() throws Exception {
268     Directory dir = newDirectory();
269     final SnapshotDeletionPolicy sdp = getDeletionPolicy();
270     final IndexWriter writer = new IndexWriter(dir, getConfig(random, sdp));
271
272     Thread[] threads = new Thread[10];
273     for (int i = 0; i < threads.length; i++) {
274       threads[i] = new Thread() {
275         @Override
276         public void run() {
277           try {
278             writer.addDocument(new Document());
279             writer.commit();
280             sdp.snapshot(getName());
281           } catch (Exception e) {
282             throw new RuntimeException(e);
283           }
284         }
285       };
286       threads[i].setName("t" + i);
287     }
288     
289     for (Thread t : threads) {
290       t.start();
291     }
292     
293     for (Thread t : threads) {
294       t.join();
295     }
296
297     // Do one last commit, so that after we release all snapshots, we stay w/ one commit
298     writer.addDocument(new Document());
299     writer.commit();
300     
301     for (Thread t : threads) {
302       sdp.release(t.getName());
303       writer.deleteUnusedFiles();
304     }
305     assertEquals(1, IndexReader.listCommits(dir).size());
306     writer.close();
307     dir.close();
308   }
309
310   @Test
311   public void testRollbackToOldSnapshot() throws Exception {
312     int numSnapshots = 2;
313     Directory dir = newDirectory();
314     SnapshotDeletionPolicy sdp = getDeletionPolicy();
315     IndexWriter writer = new IndexWriter(dir, getConfig(random, sdp));
316     prepareIndexAndSnapshots(sdp, writer, numSnapshots, "snapshot");
317     writer.close();
318
319     // now open the writer on "snapshot0" - make sure it succeeds
320     writer = new IndexWriter(dir, getConfig(random, sdp).setIndexCommit(sdp.getSnapshot("snapshot0")));
321     // this does the actual rollback
322     writer.commit();
323     writer.deleteUnusedFiles();
324     assertSnapshotExists(dir, sdp, numSnapshots - 1);
325     writer.close();
326     
327     // but 'snapshot1' files will still exist (need to release snapshot before they can be deleted).
328     String segFileName = sdp.getSnapshot("snapshot1").getSegmentsFileName();
329     assertTrue("snapshot files should exist in the directory: " + segFileName, dir.fileExists(segFileName));
330     dir.close();
331   }
332
333   @Test
334   public void testReleaseSnapshot() throws Exception {
335     Directory dir = newDirectory();
336     SnapshotDeletionPolicy sdp = getDeletionPolicy();
337     IndexWriter writer = new IndexWriter(dir, getConfig(random, sdp));
338     prepareIndexAndSnapshots(sdp, writer, 1, "snapshot");
339     
340     // Create another commit - we must do that, because otherwise the "snapshot"
341     // files will still remain in the index, since it's the last commit.
342     writer.addDocument(new Document());
343     writer.commit();
344     
345     // Release
346     String snapId = "snapshot0";
347     String segFileName = sdp.getSnapshot(snapId).getSegmentsFileName();
348     sdp.release(snapId);
349     try {
350       sdp.getSnapshot(snapId);
351       fail("should not have succeeded to get an unsnapshotted id");
352     } catch (IllegalStateException e) {
353       // expected
354     }
355     assertNull(sdp.getSnapshots().get(snapId));
356     writer.deleteUnusedFiles();
357     writer.close();
358     assertFalse("segments file should not be found in dirctory: " + segFileName, dir.fileExists(segFileName));
359     dir.close();
360   }
361
362   @Test
363   public void testExistingSnapshots() throws Exception {
364     // Tests the ability to construct a SDP from existing snapshots, and
365     // asserts that those snapshots/commit points are protected.
366     int numSnapshots = 3;
367     Directory dir = newDirectory();
368     SnapshotDeletionPolicy sdp = getDeletionPolicy();
369     IndexWriter writer = new IndexWriter(dir, getConfig(random, sdp));
370     prepareIndexAndSnapshots(sdp, writer, numSnapshots, "snapshot");
371     writer.close();
372
373     // Make a new policy and initialize with snapshots.
374     sdp = getDeletionPolicy(sdp.getSnapshots());
375     writer = new IndexWriter(dir, getConfig(random, sdp));
376     // attempt to delete unused files - the snapshotted files should not be deleted
377     writer.deleteUnusedFiles();
378     writer.close();
379     assertSnapshotExists(dir, sdp, numSnapshots);
380     dir.close();
381   }
382
383   @Test
384   public void testSnapshotLastCommitTwice() throws Exception {
385     Directory dir = newDirectory();
386     SnapshotDeletionPolicy sdp = getDeletionPolicy();
387     IndexWriter writer = new IndexWriter(dir, getConfig(random, sdp));
388     writer.addDocument(new Document());
389     writer.commit();
390     
391     String s1 = "s1";
392     String s2 = "s2";
393     IndexCommit ic1 = sdp.snapshot(s1);
394     IndexCommit ic2 = sdp.snapshot(s2);
395     assertTrue(ic1 == ic2); // should be the same instance
396     
397     // create another commit
398     writer.addDocument(new Document());
399     writer.commit();
400     
401     // release "s1" should not delete "s2"
402     sdp.release(s1);
403     writer.deleteUnusedFiles();
404     checkSnapshotExists(dir, ic2);
405     
406     writer.close();
407     dir.close();
408   }
409   
410   @Test
411   public void testMissingCommits() throws Exception {
412     // Tests the behavior of SDP when commits that are given at ctor are missing
413     // on onInit().
414     Directory dir = newDirectory();
415     SnapshotDeletionPolicy sdp = getDeletionPolicy();
416     IndexWriter writer = new IndexWriter(dir, getConfig(random, sdp));
417     writer.addDocument(new Document());
418     writer.commit();
419     IndexCommit ic = sdp.snapshot("s1");
420
421     // create another commit, not snapshotted.
422     writer.addDocument(new Document());
423     writer.close();
424
425     // open a new writer w/ KeepOnlyLastCommit policy, so it will delete "s1"
426     // commit.
427     new IndexWriter(dir, getConfig(random, null)).close();
428     
429     assertFalse("snapshotted commit should not exist", dir.fileExists(ic.getSegmentsFileName()));
430     
431     // Now reinit SDP from the commits in the index - the snapshot id should not
432     // exist anymore.
433     sdp = getDeletionPolicy(sdp.getSnapshots());
434     new IndexWriter(dir, getConfig(random, sdp)).close();
435     
436     try {
437       sdp.getSnapshot("s1");
438       fail("snapshot s1 should not exist");
439     } catch (IllegalStateException e) {
440       // expected.
441     }
442     dir.close();
443   }
444
445 }