private static Access remove(final byte[] min, final byte[] max) { assert min == null || max == null || ByteUtil.compare(min, max) <= 0; return new Access("REM", min, max) { @Override public void apply(KVStore kv) { kv.removeRange(min, max); } }; }
private static Access getRange( final byte[] min, final byte[] max, final boolean reverse, final KeyRanges removes) { assert min == null || max == null || ByteUtil.compare(min, max) <= 0; return new Access("GETRNG", min, max, reverse, removes) { @Override public void apply(KVStore kv) { for (Iterator<KVPair> i = kv.getRange(min, max, reverse); i.hasNext(); ) { final KVPair pair = i.next(); if (removes != null && removes.contains(pair.getKey())) i.remove(); } } }; }
@Override public synchronized void mutate(Mutations mutations, boolean sync) { Preconditions.checkArgument(mutations != null, "null mutations"); Preconditions.checkState(this.db != null, "closed"); // Apply mutations in a batch try (WriteBatch batch = this.db.createWriteBatch()) { // Apply removes final ReadOptions iteratorOptions = new ReadOptions().verifyChecksums(this.options.verifyChecksums()).fillCache(false); for (KeyRange range : mutations.getRemoveRanges()) { final byte[] min = range.getMin(); final byte[] max = range.getMax(); if (min != null && max != null && ByteUtil.isConsecutive(min, max)) batch.delete(min); else { try (LevelDBKVStore.Iterator i = this.kv.createIterator(iteratorOptions, min, max, false)) { while (i.hasNext()) batch.delete(i.next().getKey()); } } } // Apply puts for (Map.Entry<byte[], byte[]> entry : mutations.getPutPairs()) batch.put(entry.getKey(), entry.getValue()); // Convert counter adjustments into puts final Function<Map.Entry<byte[], Long>, Map.Entry<byte[], byte[]>> counterPutFunction = new Function<Map.Entry<byte[], Long>, Map.Entry<byte[], byte[]>>() { @Override public Map.Entry<byte[], byte[]> apply(Map.Entry<byte[], Long> adjust) { // Decode old value final byte[] key = adjust.getKey(); final long diff = adjust.getValue(); byte[] oldBytes = LevelDBAtomicKVStore.this.kv.get(key); if (oldBytes == null) oldBytes = new byte[8]; final long oldValue; try { oldValue = LevelDBAtomicKVStore.this.kv.decodeCounter(oldBytes); } catch (IllegalArgumentException e) { return null; } // Add adjustment and re-encode it return new AbstractMap.SimpleEntry<byte[], byte[]>( key, LevelDBAtomicKVStore.this.kv.encodeCounter(oldValue + diff)); } }; // Apply counter adjustments for (Map.Entry<byte[], byte[]> entry : Iterables.transform(mutations.getAdjustPairs(), counterPutFunction)) { if (entry != null) batch.put(entry.getKey(), entry.getValue()); } // Write the batch this.db.write(batch, new WriteOptions().sync(sync)); } catch (IOException e) { throw new DBException("error applying changes to LevelDB", e); } }
@Test public void testRandomWrites() throws Exception { KVStore kvstore = new NavigableMapKVStore(); KVStore expected = new NavigableMapKVStore(); MutableView mv = new MutableView(kvstore); for (int i = 0; i < 100000; i++) { // Get key(s) and value byte[] minKey; byte[] maxKey; do { minKey = new byte[1 << this.random.nextInt(4)]; this.random.nextBytes(minKey); maxKey = this.random.nextInt(7) != 0 ? new byte[1 << this.random.nextInt(4)] : null; if (maxKey != null) this.random.nextBytes(maxKey); } while (maxKey != null && ByteUtil.compare(maxKey, minKey) < 0); byte[] value = new byte[1 << this.random.nextInt(2)]; this.random.nextBytes(value); // Mutate final int choice = this.random.nextInt(85); if (choice < 10) { value = mv.get(minKey); byte[] evalue = expected.get(minKey); Assert.assertEquals(value, evalue); } else if (choice < 20) { KVPair pair = mv.getAtLeast(minKey); KVPair epair = expected.getAtLeast(minKey); Assert.assertEquals(pair, epair); } else if (choice < 30) { KVPair pair = mv.getAtMost(minKey); KVPair epair = expected.getAtMost(minKey); Assert.assertEquals(pair, epair); } else if (choice < 40) { final boolean reverse = this.random.nextBoolean(); if (this.random.nextInt(10) == 0) minKey = null; if (this.random.nextInt(10) == 0) maxKey = null; final List<KVPair> alist = Lists.newArrayList(mv.getRange(minKey, maxKey, reverse)); final List<KVPair> elist = Lists.newArrayList(expected.getRange(minKey, maxKey, reverse)); Assert.assertEquals( alist, elist, "iterations differ:\n alist=" + alist + "\n elist=" + elist + "\n"); } else if (choice < 60) { mv.put(minKey, value); expected.put(minKey, value); if (this.log.isTraceEnabled()) this.log.trace("PUT: " + ByteUtil.toString(minKey) + " -> " + ByteUtil.toString(value)); } else if (choice < 70) { mv.remove(minKey); expected.remove(minKey); if (this.log.isTraceEnabled()) this.log.trace("REMOVE: " + ByteUtil.toString(minKey)); } else if (choice < 80) { mv.removeRange(minKey, maxKey); expected.removeRange(minKey, maxKey); if (this.log.isTraceEnabled()) this.log.trace( "REMOVE_RANGE: " + ByteUtil.toString(minKey) + ", " + ByteUtil.toString(maxKey)); } else { mv.getWrites().applyTo(kvstore); mv = new MutableView(kvstore); } // Verify final boolean reverse = this.random.nextBoolean(); final List<KVPair> alist = Lists.newArrayList(mv.getRange(null, null, reverse)); final List<KVPair> elist = Lists.newArrayList(expected.getRange(null, null, reverse)); Assert.assertEquals( alist, elist, "contents differ:\n alist=" + alist + "\n elist=" + elist + "\n"); } }
@Override public void write(ByteWriter writer, Float value) { int bits = Float.floatToIntBits(value); bits ^= (bits & SIGN_BIT) != 0 ? NEG_XOR : POS_XOR; ByteUtil.writeInt(writer, bits); }
@Override public Float read(ByteReader reader) { int bits = ByteUtil.readInt(reader); bits ^= (bits & SIGN_BIT) == 0 ? NEG_XOR : POS_XOR; return Float.intBitsToFloat(bits); }