Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Licensed to the Apache Software Foundation (ASF) under one or more
0003  * contributor license agreements.  See the NOTICE file distributed with
0004  * this work for additional information regarding copyright ownership.
0005  * The ASF licenses this file to You under the Apache License, Version 2.0
0006  * (the "License"); you may not use this file except in compliance with
0007  * the License.  You may obtain a copy of the License at
0008  *
0009  *    http://www.apache.org/licenses/LICENSE-2.0
0010  *
0011  * Unless required by applicable law or agreed to in writing, software
0012  * distributed under the License is distributed on an "AS IS" BASIS,
0013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014  * See the License for the specific language governing permissions and
0015  * limitations under the License.
0016  */
0017 
0018 package org.apache.spark.util.kvstore;
0019 
0020 import java.io.File;
0021 import java.util.Arrays;
0022 import java.util.List;
0023 import java.util.NoSuchElementException;
0024 import java.util.stream.Collectors;
0025 import java.util.stream.StreamSupport;
0026 
0027 import com.google.common.collect.ImmutableSet;
0028 import org.apache.commons.io.FileUtils;
0029 import org.iq80.leveldb.DBIterator;
0030 import org.junit.After;
0031 import org.junit.Before;
0032 import org.junit.Test;
0033 import static org.junit.Assert.*;
0034 
0035 public class LevelDBSuite {
0036 
0037   private LevelDB db;
0038   private File dbpath;
0039 
0040   @After
0041   public void cleanup() throws Exception {
0042     if (db != null) {
0043       db.close();
0044     }
0045     if (dbpath != null) {
0046       FileUtils.deleteQuietly(dbpath);
0047     }
0048   }
0049 
0050   @Before
0051   public void setup() throws Exception {
0052     dbpath = File.createTempFile("test.", ".ldb");
0053     dbpath.delete();
0054     db = new LevelDB(dbpath);
0055   }
0056 
0057   @Test
0058   public void testReopenAndVersionCheckDb() throws Exception {
0059     db.close();
0060     db = null;
0061     assertTrue(dbpath.exists());
0062 
0063     db = new LevelDB(dbpath);
0064     assertEquals(LevelDB.STORE_VERSION,
0065       db.serializer.deserializeLong(db.db().get(LevelDB.STORE_VERSION_KEY)));
0066     db.db().put(LevelDB.STORE_VERSION_KEY, db.serializer.serialize(LevelDB.STORE_VERSION + 1));
0067     db.close();
0068     db = null;
0069 
0070     try {
0071       db = new LevelDB(dbpath);
0072       fail("Should have failed version check.");
0073     } catch (UnsupportedStoreVersionException e) {
0074       // Expected.
0075     }
0076   }
0077 
0078   @Test
0079   public void testObjectWriteReadDelete() throws Exception {
0080     CustomType1 t = createCustomType1(1);
0081 
0082     try {
0083       db.read(CustomType1.class, t.key);
0084       fail("Expected exception for non-existent object.");
0085     } catch (NoSuchElementException nsee) {
0086       // Expected.
0087     }
0088 
0089     db.write(t);
0090     assertEquals(t, db.read(t.getClass(), t.key));
0091     assertEquals(1L, db.count(t.getClass()));
0092 
0093     db.delete(t.getClass(), t.key);
0094     try {
0095       db.read(t.getClass(), t.key);
0096       fail("Expected exception for deleted object.");
0097     } catch (NoSuchElementException nsee) {
0098       // Expected.
0099     }
0100 
0101     // Look into the actual DB and make sure that all the keys related to the type have been
0102     // removed.
0103     assertEquals(0, countKeys(t.getClass()));
0104   }
0105 
0106   @Test
0107   public void testMultipleObjectWriteReadDelete() throws Exception {
0108     CustomType1 t1 = createCustomType1(1);
0109     CustomType1 t2 = createCustomType1(2);
0110     t2.id = t1.id;
0111 
0112     db.write(t1);
0113     db.write(t2);
0114 
0115     assertEquals(t1, db.read(t1.getClass(), t1.key));
0116     assertEquals(t2, db.read(t2.getClass(), t2.key));
0117     assertEquals(2L, db.count(t1.getClass()));
0118 
0119     // There should be one "id" index entry with two values.
0120     assertEquals(2, db.count(t1.getClass(), "id", t1.id));
0121 
0122     // Delete the first entry; now there should be 3 remaining keys, since one of the "name"
0123     // index entries should have been removed.
0124     db.delete(t1.getClass(), t1.key);
0125 
0126     // Make sure there's a single entry in the "id" index now.
0127     assertEquals(1, db.count(t2.getClass(), "id", t2.id));
0128 
0129     // Delete the remaining entry, make sure all data is gone.
0130     db.delete(t2.getClass(), t2.key);
0131     assertEquals(0, countKeys(t2.getClass()));
0132   }
0133 
0134   @Test
0135   public void testMultipleTypesWriteReadDelete() throws Exception {
0136     CustomType1 t1 = createCustomType1(1);
0137 
0138     IntKeyType t2 = new IntKeyType();
0139     t2.key = 2;
0140     t2.id = "2";
0141     t2.values = Arrays.asList("value1", "value2");
0142 
0143     ArrayKeyIndexType t3 = new ArrayKeyIndexType();
0144     t3.key = new int[] { 42, 84 };
0145     t3.id = new String[] { "id1", "id2" };
0146 
0147     db.write(t1);
0148     db.write(t2);
0149     db.write(t3);
0150 
0151     assertEquals(t1, db.read(t1.getClass(), t1.key));
0152     assertEquals(t2, db.read(t2.getClass(), t2.key));
0153     assertEquals(t3, db.read(t3.getClass(), t3.key));
0154 
0155     // There should be one "id" index with a single entry for each type.
0156     assertEquals(1, db.count(t1.getClass(), "id", t1.id));
0157     assertEquals(1, db.count(t2.getClass(), "id", t2.id));
0158     assertEquals(1, db.count(t3.getClass(), "id", t3.id));
0159 
0160     // Delete the first entry; this should not affect the entries for the second type.
0161     db.delete(t1.getClass(), t1.key);
0162     assertEquals(0, countKeys(t1.getClass()));
0163     assertEquals(1, db.count(t2.getClass(), "id", t2.id));
0164     assertEquals(1, db.count(t3.getClass(), "id", t3.id));
0165 
0166     // Delete the remaining entries, make sure all data is gone.
0167     db.delete(t2.getClass(), t2.key);
0168     assertEquals(0, countKeys(t2.getClass()));
0169 
0170     db.delete(t3.getClass(), t3.key);
0171     assertEquals(0, countKeys(t3.getClass()));
0172   }
0173 
0174   @Test
0175   public void testMetadata() throws Exception {
0176     assertNull(db.getMetadata(CustomType1.class));
0177 
0178     CustomType1 t = createCustomType1(1);
0179 
0180     db.setMetadata(t);
0181     assertEquals(t, db.getMetadata(CustomType1.class));
0182 
0183     db.setMetadata(null);
0184     assertNull(db.getMetadata(CustomType1.class));
0185   }
0186 
0187   @Test
0188   public void testUpdate() throws Exception {
0189     CustomType1 t = createCustomType1(1);
0190 
0191     db.write(t);
0192 
0193     t.name = "anotherName";
0194 
0195     db.write(t);
0196 
0197     assertEquals(1, db.count(t.getClass()));
0198     assertEquals(1, db.count(t.getClass(), "name", "anotherName"));
0199     assertEquals(0, db.count(t.getClass(), "name", "name"));
0200   }
0201 
0202   @Test
0203   public void testRemoveAll() throws Exception {
0204     for (int i = 0; i < 2; i++) {
0205       for (int j = 0; j < 2; j++) {
0206         ArrayKeyIndexType o = new ArrayKeyIndexType();
0207         o.key = new int[] { i, j, 0 };
0208         o.id = new String[] { "things" };
0209         db.write(o);
0210 
0211         o = new ArrayKeyIndexType();
0212         o.key = new int[] { i, j, 1 };
0213         o.id = new String[] { "more things" };
0214         db.write(o);
0215       }
0216     }
0217 
0218     ArrayKeyIndexType o = new ArrayKeyIndexType();
0219     o.key = new int[] { 2, 2, 2 };
0220     o.id = new String[] { "things" };
0221     db.write(o);
0222 
0223     assertEquals(9, db.count(ArrayKeyIndexType.class));
0224 
0225     db.removeAllByIndexValues(
0226       ArrayKeyIndexType.class,
0227       KVIndex.NATURAL_INDEX_NAME,
0228       ImmutableSet.of(new int[] {0, 0, 0}, new int[] { 2, 2, 2 }));
0229     assertEquals(7, db.count(ArrayKeyIndexType.class));
0230 
0231     db.removeAllByIndexValues(
0232       ArrayKeyIndexType.class,
0233       "id",
0234       ImmutableSet.of(new String[] { "things" }));
0235     assertEquals(4, db.count(ArrayKeyIndexType.class));
0236 
0237     db.removeAllByIndexValues(
0238       ArrayKeyIndexType.class,
0239       "id",
0240       ImmutableSet.of(new String[] { "more things" }));
0241     assertEquals(0, db.count(ArrayKeyIndexType.class));
0242   }
0243 
0244   @Test
0245   public void testSkip() throws Exception {
0246     for (int i = 0; i < 10; i++) {
0247       db.write(createCustomType1(i));
0248     }
0249 
0250     KVStoreIterator<CustomType1> it = db.view(CustomType1.class).closeableIterator();
0251     assertTrue(it.hasNext());
0252     assertTrue(it.skip(5));
0253     assertEquals("key5", it.next().key);
0254     assertTrue(it.skip(3));
0255     assertEquals("key9", it.next().key);
0256     assertFalse(it.hasNext());
0257   }
0258 
0259   @Test
0260   public void testNegativeIndexValues() throws Exception {
0261     List<Integer> expected = Arrays.asList(-100, -50, 0, 50, 100);
0262 
0263     expected.forEach(i -> {
0264       try {
0265         db.write(createCustomType1(i));
0266       } catch (Exception e) {
0267         throw new RuntimeException(e);
0268       }
0269     });
0270 
0271     List<Integer> results = StreamSupport
0272       .stream(db.view(CustomType1.class).index("int").spliterator(), false)
0273       .map(e -> e.num)
0274       .collect(Collectors.toList());
0275 
0276     assertEquals(expected, results);
0277   }
0278 
0279   private CustomType1 createCustomType1(int i) {
0280     CustomType1 t = new CustomType1();
0281     t.key = "key" + i;
0282     t.id = "id" + i;
0283     t.name = "name" + i;
0284     t.num = i;
0285     t.child = "child" + i;
0286     return t;
0287   }
0288 
0289   private int countKeys(Class<?> type) throws Exception {
0290     byte[] prefix = db.getTypeInfo(type).keyPrefix();
0291     int count = 0;
0292 
0293     DBIterator it = db.db().iterator();
0294     it.seek(prefix);
0295 
0296     while (it.hasNext()) {
0297       byte[] key = it.next().getKey();
0298       if (LevelDBIterator.startsWith(key, prefix)) {
0299         count++;
0300       }
0301     }
0302 
0303     return count;
0304   }
0305 
0306   public static class IntKeyType {
0307 
0308     @KVIndex
0309     public int key;
0310 
0311     @KVIndex("id")
0312     public String id;
0313 
0314     public List<String> values;
0315 
0316     @Override
0317     public boolean equals(Object o) {
0318       if (o instanceof IntKeyType) {
0319         IntKeyType other = (IntKeyType) o;
0320         return key == other.key && id.equals(other.id) && values.equals(other.values);
0321       }
0322       return false;
0323     }
0324 
0325     @Override
0326     public int hashCode() {
0327       return id.hashCode();
0328     }
0329 
0330   }
0331 
0332 }