From 23fd75f27c40462636f09920719b5dcbef5b8f36 Mon Sep 17 00:00:00 2001 From: Robert Stupp Date: Sun, 5 Jul 2015 22:14:36 +0700 Subject: [PATCH] Bloom filter false positive ratio is not honoured patch by Robert Stupp, reviewed by Benedict Elliott-Smith for CASSANDRA-8413 --- CHANGES.txt | 1 + NEWS.txt | 3 + .../io/sstable/format/SSTableReader.java | 8 +- .../cassandra/io/sstable/format/Version.java | 8 +- .../io/sstable/format/big/BigFormat.java | 19 ++- .../io/sstable/format/big/BigTableWriter.java | 2 +- .../apache/cassandra/utils/BloomFilter.java | 31 +++- .../utils/BloomFilterSerializer.java | 24 ++- .../apache/cassandra/utils/FilterFactory.java | 18 +- .../org/apache/cassandra/utils/IFilter.java | 2 +- test/data/bloom-filter/ka/foo.cql | 64 +++++++ .../ka/foo/foo-atable-ka-1-CompressionInfo.db | Bin 0 -> 43 bytes .../ka/foo/foo-atable-ka-1-Data.db | Bin 0 -> 256 bytes .../ka/foo/foo-atable-ka-1-Digest.sha1 | 1 + .../ka/foo/foo-atable-ka-1-Filter.db | Bin 0 -> 24 bytes .../ka/foo/foo-atable-ka-1-Index.db | Bin 0 -> 180 bytes .../ka/foo/foo-atable-ka-1-Statistics.db | Bin 0 -> 4468 bytes .../ka/foo/foo-atable-ka-1-Summary.db | Bin 0 -> 80 bytes .../ka/foo/foo-atable-ka-1-TOC.txt | 8 + .../la/foo/la-1-big-CompressionInfo.db | Bin 0 -> 43 bytes .../data/bloom-filter/la/foo/la-1-big-Data.db | Bin 0 -> 244 bytes .../la/foo/la-1-big-Digest.adler32 | 1 + .../bloom-filter/la/foo/la-1-big-Filter.db | Bin 0 -> 24 bytes .../bloom-filter/la/foo/la-1-big-Index.db | Bin 0 -> 180 bytes .../la/foo/la-1-big-Statistics.db | Bin 0 -> 4468 bytes .../bloom-filter/la/foo/la-1-big-Summary.db | Bin 0 -> 80 bytes .../data/bloom-filter/la/foo/la-1-big-TOC.txt | 8 + .../serialization/3.0/utils.BloomFilter.bin | Bin 0 -> 2500016 bytes .../3.0/utils.BloomFilter1000.bin | Bin 0 -> 2500016 bytes .../3.0/utils.EstimatedHistogram.bin | Bin 0 -> 97500 bytes .../cassandra/utils/LongBloomFilterTest.java | 159 ++++++++++++++++-- .../AbstractSerializationsTester.java | 16 +- .../apache/cassandra/utils/BitSetTest.java | 11 +- .../cassandra/utils/BloomFilterTest.java | 118 +++++++++---- .../cassandra/utils/SerializationsTest.java | 148 +++++++++++++++- 35 files changed, 551 insertions(+), 99 deletions(-) create mode 100644 test/data/bloom-filter/ka/foo.cql create mode 100644 test/data/bloom-filter/ka/foo/foo-atable-ka-1-CompressionInfo.db create mode 100644 test/data/bloom-filter/ka/foo/foo-atable-ka-1-Data.db create mode 100644 test/data/bloom-filter/ka/foo/foo-atable-ka-1-Digest.sha1 create mode 100644 test/data/bloom-filter/ka/foo/foo-atable-ka-1-Filter.db create mode 100644 test/data/bloom-filter/ka/foo/foo-atable-ka-1-Index.db create mode 100644 test/data/bloom-filter/ka/foo/foo-atable-ka-1-Statistics.db create mode 100644 test/data/bloom-filter/ka/foo/foo-atable-ka-1-Summary.db create mode 100644 test/data/bloom-filter/ka/foo/foo-atable-ka-1-TOC.txt create mode 100644 test/data/bloom-filter/la/foo/la-1-big-CompressionInfo.db create mode 100644 test/data/bloom-filter/la/foo/la-1-big-Data.db create mode 100644 test/data/bloom-filter/la/foo/la-1-big-Digest.adler32 create mode 100644 test/data/bloom-filter/la/foo/la-1-big-Filter.db create mode 100644 test/data/bloom-filter/la/foo/la-1-big-Index.db create mode 100644 test/data/bloom-filter/la/foo/la-1-big-Statistics.db create mode 100644 test/data/bloom-filter/la/foo/la-1-big-Summary.db create mode 100644 test/data/bloom-filter/la/foo/la-1-big-TOC.txt create mode 100644 test/data/serialization/3.0/utils.BloomFilter.bin create mode 100644 test/data/serialization/3.0/utils.BloomFilter1000.bin create mode 100644 test/data/serialization/3.0/utils.EstimatedHistogram.bin diff --git a/CHANGES.txt b/CHANGES.txt index a1018e3231..bb3141ee14 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.0 + * Bloom filter false positive ratio is not honoured (CASSANDRA-8413) * New option for cassandra-stress to leave a ratio of columns null (CASSANDRA-9522) * Change hinted_handoff_enabled yaml setting, JMX (CASSANDRA-9035) * Add algorithmic token allocation (CASSANDRA-7032) diff --git a/NEWS.txt b/NEWS.txt index 3a9fe6af04..54ed7c6ada 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -18,6 +18,9 @@ using the provided 'sstableupgrade' tool. Upgrading --------- + _ New SSTable version 'la' with improved bloom-filter false-positive handling + compared to previous version 'ka' used in 2.2 and 2.1. Running sstableupgrade + is not necessary but recommended. - Pig's CassandraStorage has been removed. Use CqlNativeStorage instead. - Hadoop BulkOutputFormat and BulkRecordWriter have been removed; use CqlBulkOutputFormat and CqlBulkRecordWriter instead. diff --git a/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java b/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java index deff4c147f..ab357a553d 100644 --- a/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java +++ b/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java @@ -657,7 +657,7 @@ public abstract class SSTableReader extends SSTable implements SelfRefCounted= 0; storeRows = version.compareTo("la") >= 0; correspondingMessagingVersion = storeRows ? MessagingService.VERSION_30 : MessagingService.VERSION_21; + hasOldBfHashOrder = version.compareTo("la") < 0; } @Override @@ -181,6 +182,12 @@ public class BigFormat implements SSTableFormat return tracksLegacyCounterShards; } + @Override + public boolean hasOldBfHashOrder() + { + return hasOldBfHashOrder; + } + @Override public boolean hasNewFileName() { diff --git a/src/java/org/apache/cassandra/io/sstable/format/big/BigTableWriter.java b/src/java/org/apache/cassandra/io/sstable/format/big/BigTableWriter.java index e10beb0816..a991d99254 100644 --- a/src/java/org/apache/cassandra/io/sstable/format/big/BigTableWriter.java +++ b/src/java/org/apache/cassandra/io/sstable/format/big/BigTableWriter.java @@ -428,7 +428,7 @@ public class BigTableWriter extends SSTableWriter indexFile = SequentialWriter.open(new File(descriptor.filenameFor(Component.PRIMARY_INDEX))); builder = SegmentedFile.getBuilder(DatabaseDescriptor.getIndexAccessMode(), false); summary = new IndexSummaryBuilder(keyCount, metadata.getMinIndexInterval(), Downsampling.BASE_SAMPLING_LEVEL); - bf = FilterFactory.getFilter(keyCount, metadata.getBloomFilterFpChance(), true); + bf = FilterFactory.getFilter(keyCount, metadata.getBloomFilterFpChance(), true, descriptor.version.hasOldBfHashOrder()); // register listeners to be alerted when the data files are flushed indexFile.setPostFlushListener(new Runnable() { diff --git a/src/java/org/apache/cassandra/utils/BloomFilter.java b/src/java/org/apache/cassandra/utils/BloomFilter.java index dbd489f0fd..e1d9f209fe 100644 --- a/src/java/org/apache/cassandra/utils/BloomFilter.java +++ b/src/java/org/apache/cassandra/utils/BloomFilter.java @@ -34,26 +34,31 @@ public class BloomFilter extends WrappedSharedCloseable implements IFilter public final IBitSet bitset; public final int hashCount; + /** + * CASSANDRA-8413: 3.0 (inverted) bloom filters have no 'static' bits caused by using the same upper bits + * for both bloom filter and token distribution. + */ + public final boolean oldBfHashOrder; - BloomFilter(int hashCount, IBitSet bitset) + BloomFilter(int hashCount, IBitSet bitset, boolean oldBfHashOrder) { super(bitset); this.hashCount = hashCount; this.bitset = bitset; + this.oldBfHashOrder = oldBfHashOrder; } - BloomFilter(BloomFilter copy) + private BloomFilter(BloomFilter copy) { super(copy); this.hashCount = copy.hashCount; this.bitset = copy.bitset; + this.oldBfHashOrder = copy.oldBfHashOrder; } - public static final BloomFilterSerializer serializer = new BloomFilterSerializer(); - public long serializedSize() { - return serializer.serializedSize(this); + return BloomFilterSerializer.serializedSize(this); } // Murmur is faster than an SHA-based approach and provides as-good collision @@ -70,7 +75,7 @@ public class BloomFilter extends WrappedSharedCloseable implements IFilter long[] hash = new long[2]; key.filterHash(hash); long[] indexes = new long[hashCount]; - setIndexes(hash[0], hash[1], hashCount, max, indexes); + setIndexes(hash[1], hash[0], hashCount, max, indexes); return indexes; } @@ -84,12 +89,19 @@ public class BloomFilter extends WrappedSharedCloseable implements IFilter // so that we do not need to allocate two arrays. long[] indexes = reusableIndexes.get(); key.filterHash(indexes); - setIndexes(indexes[0], indexes[1], hashCount, bitset.capacity(), indexes); + setIndexes(indexes[1], indexes[0], hashCount, bitset.capacity(), indexes); return indexes; } private void setIndexes(long base, long inc, int count, long max, long[] results) { + if (oldBfHashOrder) + { + long x = inc; + inc = base; + base = x; + } + for (int i = 0; i < count; i++) { results[i] = FBUtilities.abs(base % max); @@ -134,4 +146,9 @@ public class BloomFilter extends WrappedSharedCloseable implements IFilter { return bitset.offHeapSize(); } + + public String toString() + { + return "BloomFilter[hashCount=" + hashCount + ";oldBfHashOrder=" + oldBfHashOrder + ";capacity=" + bitset.capacity() + ']'; + } } diff --git a/src/java/org/apache/cassandra/utils/BloomFilterSerializer.java b/src/java/org/apache/cassandra/utils/BloomFilterSerializer.java index 00bb153e18..6f57fc8f4e 100644 --- a/src/java/org/apache/cassandra/utils/BloomFilterSerializer.java +++ b/src/java/org/apache/cassandra/utils/BloomFilterSerializer.java @@ -21,37 +21,35 @@ import java.io.DataInput; import java.io.IOException; import org.apache.cassandra.db.TypeSizes; -import org.apache.cassandra.io.ISerializer; -import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; import org.apache.cassandra.utils.obs.IBitSet; import org.apache.cassandra.utils.obs.OffHeapBitSet; import org.apache.cassandra.utils.obs.OpenBitSet; -class BloomFilterSerializer implements ISerializer +final class BloomFilterSerializer { - public void serialize(BloomFilter bf, DataOutputPlus out) throws IOException + private BloomFilterSerializer() + { + } + + public static void serialize(BloomFilter bf, DataOutputPlus out) throws IOException { out.writeInt(bf.hashCount); bf.bitset.serialize(out); } - public BloomFilter deserialize(DataInputPlus in) throws IOException + public static BloomFilter deserialize(DataInput in, boolean oldBfHashOrder) throws IOException { - return deserialize(in, false); + return deserialize(in, false, oldBfHashOrder); } @SuppressWarnings("resource") - public BloomFilter deserialize(DataInput in, boolean offheap) throws IOException + public static BloomFilter deserialize(DataInput in, boolean offheap, boolean oldBfHashOrder) throws IOException { int hashes = in.readInt(); IBitSet bs = offheap ? OffHeapBitSet.deserialize(in) : OpenBitSet.deserialize(in); - return createFilter(hashes, bs); - } - BloomFilter createFilter(int hashes, IBitSet bs) - { - return new BloomFilter(hashes, bs); + return new BloomFilter(hashes, bs, oldBfHashOrder); } /** @@ -61,7 +59,7 @@ class BloomFilterSerializer implements ISerializer * * @return serialized size of the given bloom filter */ - public long serializedSize(BloomFilter bf) + public static long serializedSize(BloomFilter bf) { int size = TypeSizes.sizeof(bf.hashCount); // hash count size += bf.bitset.serializedSize(); diff --git a/src/java/org/apache/cassandra/utils/FilterFactory.java b/src/java/org/apache/cassandra/utils/FilterFactory.java index d77500c32f..869f3fa3a4 100644 --- a/src/java/org/apache/cassandra/utils/FilterFactory.java +++ b/src/java/org/apache/cassandra/utils/FilterFactory.java @@ -37,19 +37,19 @@ public class FilterFactory public static void serialize(IFilter bf, DataOutputPlus output) throws IOException { - BloomFilter.serializer.serialize((BloomFilter) bf, output); + BloomFilterSerializer.serialize((BloomFilter) bf, output); } - public static IFilter deserialize(DataInput input, boolean offheap) throws IOException + public static IFilter deserialize(DataInput input, boolean offheap, boolean oldBfHashOrder) throws IOException { - return BloomFilter.serializer.deserialize(input, offheap); + return BloomFilterSerializer.deserialize(input, offheap, oldBfHashOrder); } /** * @return A BloomFilter with the lowest practical false positive * probability for the given number of elements. */ - public static IFilter getFilter(long numElements, int targetBucketsPerElem, boolean offheap) + public static IFilter getFilter(long numElements, int targetBucketsPerElem, boolean offheap, boolean oldBfHashOrder) { int maxBucketsPerElement = Math.max(1, BloomCalculations.maxBucketsPerElement(numElements)); int bucketsPerElement = Math.min(targetBucketsPerElem, maxBucketsPerElement); @@ -58,7 +58,7 @@ public class FilterFactory logger.warn(String.format("Cannot provide an optimal BloomFilter for %d elements (%d/%d buckets per element).", numElements, bucketsPerElement, targetBucketsPerElem)); } BloomCalculations.BloomSpecification spec = BloomCalculations.computeBloomSpec(bucketsPerElement); - return createFilter(spec.K, numElements, spec.bucketsPerElement, offheap); + return createFilter(spec.K, numElements, spec.bucketsPerElement, offheap, oldBfHashOrder); } /** @@ -68,21 +68,21 @@ public class FilterFactory * Asserts that the given probability can be satisfied using this * filter. */ - public static IFilter getFilter(long numElements, double maxFalsePosProbability, boolean offheap) + public static IFilter getFilter(long numElements, double maxFalsePosProbability, boolean offheap, boolean oldBfHashOrder) { assert maxFalsePosProbability <= 1.0 : "Invalid probability"; if (maxFalsePosProbability == 1.0) return new AlwaysPresentFilter(); int bucketsPerElement = BloomCalculations.maxBucketsPerElement(numElements); BloomCalculations.BloomSpecification spec = BloomCalculations.computeBloomSpec(bucketsPerElement, maxFalsePosProbability); - return createFilter(spec.K, numElements, spec.bucketsPerElement, offheap); + return createFilter(spec.K, numElements, spec.bucketsPerElement, offheap, oldBfHashOrder); } @SuppressWarnings("resource") - private static IFilter createFilter(int hash, long numElements, int bucketsPer, boolean offheap) + private static IFilter createFilter(int hash, long numElements, int bucketsPer, boolean offheap, boolean oldBfHashOrder) { long numBits = (numElements * bucketsPer) + BITSET_EXCESS; IBitSet bitset = offheap ? new OffHeapBitSet(numBits) : new OpenBitSet(numBits); - return new BloomFilter(hash, bitset); + return new BloomFilter(hash, bitset, oldBfHashOrder); } } diff --git a/src/java/org/apache/cassandra/utils/IFilter.java b/src/java/org/apache/cassandra/utils/IFilter.java index 2f59864772..b5eb2c4160 100644 --- a/src/java/org/apache/cassandra/utils/IFilter.java +++ b/src/java/org/apache/cassandra/utils/IFilter.java @@ -21,7 +21,7 @@ import org.apache.cassandra.utils.concurrent.SharedCloseable; public interface IFilter extends SharedCloseable { - public interface FilterKey + interface FilterKey { /** Places the murmur3 hash of the key in the given long array of size at least two. */ void filterHash(long[] dest); diff --git a/test/data/bloom-filter/ka/foo.cql b/test/data/bloom-filter/ka/foo.cql new file mode 100644 index 0000000000..c4aed6ab82 --- /dev/null +++ b/test/data/bloom-filter/ka/foo.cql @@ -0,0 +1,64 @@ +create keyspace foo with replication = {'class':'SimpleStrategy', 'replication_factor':1}; +use foo ; +create table atable ( pk int primary key, val int); +insert into atable (pk, val) VALUES ( 1,1); +insert into atable (pk, val) VALUES ( 2,2); +insert into atable (pk, val) VALUES ( 3,3); +insert into atable (pk, val) VALUES ( 4,4); +insert into atable (pk, val) VALUES ( 5,5); +insert into atable (pk, val) VALUES ( 6,6); +insert into atable (pk, val) VALUES ( 7,7); +insert into atable (pk, val) VALUES ( 8,8); +insert into atable (pk, val) VALUES ( 9,9); +insert into atable (pk, val) VALUES ( 10,10); + + + +[ +{"key": "5", + "cells": [["","",1428529571195019], + ["val","5",1428529571195019]]}, +{"key": "10", + "cells": [["","",1428529588242944], + ["val","10",1428529588242944]]}, +{"key": "1", + "cells": [["","",1428529563371015], + ["val","1",1428529563371015]]}, +{"key": "8", + "cells": [["","",1428529582362836], + ["val","8",1428529582362836]]}, +{"key": "2", + "cells": [["","",1428529565275080], + ["val","2",1428529565275080]]}, +{"key": "4", + "cells": [["","",1428529569331171], + ["val","4",1428529569331171]]}, +{"key": "7", + "cells": [["","",1428529575898967], + ["val","7",1428529575898967]]}, +{"key": "6", + "cells": [["","",1428529573027018], + ["val","6",1428529573027018]]}, +{"key": "9", + "cells": [["","",1428529585667042], + ["val","9",1428529585667042]]}, +{"key": "3", + "cells": [["","",1428529567379095], + ["val","3",1428529567379095]]} +] + + + +SSTable: test/data/legacy-sstables/ka/foo/foo-atable-ka-1 +Partitioner: org.apache.cassandra.dht.Murmur3Partitioner +Bloom Filter FP chance: 0,010000 +Minimum timestamp: 1428529563371015 +Maximum timestamp: 1428529588242944 +SSTable max local deletion time: 2147483647 +Compression ratio: 0.4 +Estimated droppable tombstones: 0.0 +SSTable Level: 0 +Repaired at: 0 +ReplayPosition(segmentId=1428529465658, position=6481) +Estimated tombstone drop times:%n + diff --git a/test/data/bloom-filter/ka/foo/foo-atable-ka-1-CompressionInfo.db b/test/data/bloom-filter/ka/foo/foo-atable-ka-1-CompressionInfo.db new file mode 100644 index 0000000000000000000000000000000000000000..0c9c6e6a81a82cd421cd5019441cf8141642d4cd GIT binary patch literal 43 gcmZSJ^@%cZ&d)6NxKoNN+ zkZ7rw@CmpmP(+@^o`J=#{rh8k1}3n{EG!K2>>#l>`r!~UYX$~MMh2ie*g(PuVorgD pL2ld2AP;hpZ&{=DBe>C^Koes*`f5*Sww+@)iMA8D#ZkY literal 0 HcmV?d00001 diff --git a/test/data/bloom-filter/ka/foo/foo-atable-ka-1-Index.db b/test/data/bloom-filter/ka/foo/foo-atable-ka-1-Index.db new file mode 100644 index 0000000000000000000000000000000000000000..480fd5157e27b2c7406435106617b029be39b181 GIT binary patch literal 180 zcmZQzVPIfj1!4pMiEu%=_Fx($!Uz?qgNtxLMfSo)n4ltm;36QMK)`4Xra(M)FpGh) Z3NFG171;?F;e?9(fQv9gM3~G$Gyt9z1$h7f literal 0 HcmV?d00001 diff --git a/test/data/bloom-filter/ka/foo/foo-atable-ka-1-Statistics.db b/test/data/bloom-filter/ka/foo/foo-atable-ka-1-Statistics.db new file mode 100644 index 0000000000000000000000000000000000000000..037e0b4b5900d04de218ffbc328e7cdf101827ca GIT binary patch literal 4468 zcmeI$i%*SV90&0CaxR^t4xN$;%`KO3m?TV&%dyIsl*nzIE>kvTrxPtqBQwoXhGgWn zwH6a{sWdiGE)g-wZP|`8#&IjTb-X91_4_{mf$cooyZ8C@ectvw&-*;*JUbl6iQK<3 zK)UhLwNSbQ>DtMSPBE>~8P^-*5)*WB#?(|}a=gi?i%&G`=BJs~rI`XFj3#rEIVmMM z!KBa1cpZAibCdL-z36N~2*x zZ{H=eC(;x7UC;gNf}`4Xvv-LWd)H{`?tb^*WZf0VUCvm+SjpIhv6`_fV+~_>#-5D5 z84qMUlyLy#35=&X+}+PGCSS~W4dcy>_cJbI+=%9x_rXtMax>$rXrUkDZD^7C|KefH zWzlGPAX*WERx;yt`GvW<7VT<9_uhfl9z%OAL;E(M2aQ7yKZ@4Hpo5Q~XH}si`l46+ zpi}MJnUriJfmAJG)^Du=bE_Yi_xp^FS%f}<>yx)Wfn12f{Vd!;i!t}1y-bGdU-qyL za`~+su;Nn**d@vaR&^$Tdv$t%HSxF}-3@Oc_pI;&d%yAp4{VGC53R@d9ngXAJ23Ye zv=3>11D-n*`?u8B0{PkxT5#rqQgFeK$>8!EI6p7TVLo|vpElZ_*N5%~Zyiz$zFR$> zS{QK|K3|0cn#VL3ebZrmh!rbUG?%5Trc=vTG{SflDM{%xSC(7asa1wK->7@}RlG(BG?(gXM-5-XPX-WIO#_aX<`@pAIU#TOx-|ZGHeP6@A zgobC6d`z^}pPmPT0^iqYNUm>et=vdF?{Mj9+MngtDM55SCr28Ja!LEF*z($D;=&g% ztBQ!Po;X=b`)jEVXbA4fdF}SP-?NCr*S7uI)?+T!QOD2Vo->%*kqhJ*d)0a7!U=Jn zxnMq=XD*ly&Uxm7`EZ`OoM$ewApWm27eDXNpW9P8&SxPf_XtTFt5DkS8;Y~Jt*E; z;$(zvzy06976I^t0buu4RUM$v)e*Kfo|Wlm(Z;d?U98qa*aRvEzfsGG|T3fg&k;bB_!ZNcnhqWFX^Uvv=tLB{z)$g|hAx|8R^r+<_Xh{+|4rftpfg USC0&&r7UlPoBE3y7~T8FAIq*KBLDyZ literal 0 HcmV?d00001 diff --git a/test/data/bloom-filter/la/foo/la-1-big-Digest.adler32 b/test/data/bloom-filter/la/foo/la-1-big-Digest.adler32 new file mode 100644 index 0000000000..d6952e21c1 --- /dev/null +++ b/test/data/bloom-filter/la/foo/la-1-big-Digest.adler32 @@ -0,0 +1 @@ +3417187619 \ No newline at end of file diff --git a/test/data/bloom-filter/la/foo/la-1-big-Filter.db b/test/data/bloom-filter/la/foo/la-1-big-Filter.db new file mode 100644 index 0000000000000000000000000000000000000000..533a611f3c0d0745335c219752714a1f0f8a4259 GIT binary patch literal 24 fcmZQzU|?lnU|?!_r17AEPs8lB(j~7GO%9!!GKjk_Kw6TZtJ-S&@Wk zR2Gz3frLd?cBn-`mUbT&*^R-bQPg&)6>e{H#ou@SgJ2&%-hBMd$jrQ%-9d#yAu2kq zKJtUf_bmAqA3eky{l1YuC{QXYt>|(It90x<;?g?sxu9*;#NtRh->8yL0y7tl{j*SpF&Eu1f*1wYOk(IWTv#r>Eo z=b%-gXt(ibcW%5MzcJU;qdjft?wirNQgp8c=)eYazY*v`2hoNEboc@Eq>Jd-p6I0k z=uF3UCRe^hAQ#KH^;>Vk+@=RJ{bzK-Z1e$KpUn0cav>h~vv3nFCftSkN+qs;<%8Rh ztF9M--9D9rJ>uN4-|Ajq|5t(FzKwC=}xLcZdI4xBr)0=%znEV$|_&dZ z76zY(&sSlO_7Tm+z${oFV)bG*&6Qc|3Dl}ZjWAxfjMOZeyH{C%P^(Q-zEOA6*T8=A zO3>Y>c@HtBj@lHcT-U0S;Tg=Fii3#7f!bbLZKv1nn|oy5wZd%76v*47;QepEjTchm0=_n|(`k)*yy z@u}=H#G09h>deHROA0>H@oS%YzoX;#eh^)$BlUYq`77!7LBLUCje+F)8_hcUzJk6) z#pIKG_#9g>Jr9J1zOU1g+}PMsvzB=J{)*$YKMQT+!svQVk294OlKOees_L7FOJ2OZ zSVny5@R17IUu$hhLwHxtbenGfnM543;`{H7T~=f|%lH}Gbq3QobAhyR6kTU7oDkQU z3+BUh=7Q-^xXxTKAFeZ(>&!(K#Q$~XV$(~8tZHLSe^{aNnf5TKwBs2Z9=SpOmxu9* Pw8dZkk%yN=hKR+#s*hWl literal 0 HcmV?d00001 diff --git a/test/data/bloom-filter/la/foo/la-1-big-Summary.db b/test/data/bloom-filter/la/foo/la-1-big-Summary.db new file mode 100644 index 0000000000000000000000000000000000000000..602ec06f74e8fb36fe02f037af28d3823a9d629f GIT binary patch literal 80 zcmZQzU}#`qU|!sc%+1Z)=j^Mh2oNAZfB*pk1PBlyK!5-N0t5&U zAW%}kl~5%$#Uem}009C72oNAZfB*pk1V$HlA3Z_&5gP2r!*r00t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyP)Wceh%3PgOMn0Y0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk_XS?>FS3LH z0RjXF5FkK+009C72oNAZU|E6NvXv{GKwN<>Zt5~8K!5-N0t5&UAV7cs0RjXF5cn+M z*<_zRyt*rp?k=%_0D+emzf5ff1PBlyK;XQ9^G43wS#wSSSK!UrS5*<1Ss>3`oZ6xY zl>bQi9;I}75+Fc;009C72oNAZfB*pk1PBly&_f{gnADsE2oRV+p!`9d2~d6$nk;vVXI{^X&2oNAZfB*pk1PBlyK%k<)!&{fQ z(O5-!p$QNmK!5-N0t5&UAV7dXErDDsu($*W5FkK+0D;*B9v(KCz4(^(IkCB=wz&up zm_y*Lcp2`oRh(2n0zV2k_2xT^&w}4gyhDHh0RjXF{1Wgb#b!W&009C72oNAZfB*pk z1PJ_Bp!~5>umr*jIOrI@g$f`*fB=Dk1oA++NQD3a0t5&UAh3qO5j`+~@OnL0F#!SuRub@cZl!Lk zl>h+(y9M5Mk2H_~0RjXF92H1MS1_Ky7XhDCzJPd1fB=DC0v;A>Gax{K009C79t)($ zi!CKUfB*pk1PBlyK!5-N0t5;Pq(X6oAwYlt0RjXF5FkK+009C72oNAZfB*pk1ilG; ze0#?m1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oM-SpxkZ1%0=qt;?Qn4dJ_{M zFp9tgzspCVlm`I<1PBlqL%?0}$LNDB2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjY? z38ZE-)t|`WzWT;yAV7e?fC9Gxb6P7U9ayX1swF^x009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5Fjw5KpHZuC6jVe_mXW_Gywtx2oNAZfB*pk1nLO*l3B-9R00G# z3%quo-}D3s5FkK+009C72oNAZfB*pk1PBlyK!5;&eFAykFrx?%AV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PFW=c>n&EcL)$5K!5-N0t5&UAV7cs0RjXF5FkK+009C72oNA} zSm4pUN%QY$?(lb5)d~a%5FpS;z!^1t+|5dWz+QoE??B@S5FkKcN`W+GRf-}&fB*pk z1PBlyKp?2Vqr=2OOOkd*K!5-N0t*PF#x?rL&=}lw1PBlyK!5-N0t5&UAaGycc7Ks2 z1PEjV1PBlyK!5;&KmsXHu2L-^@KGR!5CjMiAV7cs0Rm3s8h;x|!Nm_gumh9cBJfB*pkWd!_DDB~y! z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAVA=XfLpO$(X%cA0t5&UAV7cs z0Rr6xa`y}*K!5-N0<{F*Y6TXT009C72oNAZ;9r6Fe}D870RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV8q2fUBOn@|&Ch0RjZ35;*)<_3#Q)aVdrX0RjXF5FkK+ z009C72oNAZfB*pk1lkICta)2+vk@RbfB*pk&jfrrc_w5z0RjXF5FkK+009C7N(yWx zgNa3e009Dz1?>8I>|-eb0t5&UAV7csf!PIavlpm70t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0tChr@a&86dQvE(w?c7*AwYlt0RjXF5FkK+009C7<`vlH zElp)*1?*}n>nR!m0t5&g7dUi#u0w;5=VA>41PBlyK!5-N0t5&UAV7csfx7}uOukEF z0RaL82oNAZfB*pk1PBlyK!CtKfqZX)_X!XnK!5-N0!;*5HPeLIBm@W$AV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!Ct0f$h{SUn~Ly2oNAZfB=Ee0&k(SmplOi1PBlyK%j^~DiTEy0t5&U zAV7cs0RjXF5Fjvyz@X>F96h~f3`|)NAV7cs0RjZp5yl} z2oNC9SilLPjloSvfB*pk1PBlyu!6uxgCc#DZ-8tH0t5&U7+k<#!@(OQJpu#>5FkK+ z0D;8>TI>^Bj8wS<2oNAZfWR>UXOMsP;_=zTD*^-v5Fl_=;C6Hc;|UNTK!5-N0t5&U zAV7cs0RjYe3pf#cH`U&Oo|3p%YUObbAg1e_;5L009C72oNAZU;qIZ zWDU>+DG(q)fB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5Fk)jz^Qw6eO~}q1{R(G0RjXF z5FkK+0D&+9TbN8GN}#BKtH_IT3Pykc0RjXF5FkK+009C72oNAZfB*pk1PBlyK!Cu= z0`6}=a(CoLfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+KtKVvu)5YXe%Ji0O@IJ_ zUjmuJm;&3FX~~KJ0RjXF5Fl_=;PvPV#uFewfB*pk1PF{T;P}k=J(3{-0`m&^9XxMu zRYqVvfsge{Rxtqr1PBZyunm-pR0t3tK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&Um{`CQU?*;_(g+YBK!5-N0t5&UAVA=ZKsvLMbqEk3K!Cu% z0`LF+=qCaM2oNAZfB*pk1PBlyFulMweT51nK!5-N0t5;OG<}ZAtY=3%{!m~`6ylpe z{`QVH2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5V$X}-Ctx00RoE%PRG+~F z1PBlyK!5-N0tB82Y)_V3On?9Z0t5&UAV7dXA%RpVjxYoU6L=jgAL$SvK!5-N0t5&U zAV7cs0RjYm6tENHM;31qAV7cs0RjXF5FkK+zicqc+hxCSL*s2y_&1Ie15WQxhOSfB*pk1PBlyKwu^T zUogLPbNlkzO9F=l^5HeCK!5-N0{;CD z3Q2$f0RjXL3*^IVSYc3ss~@gzck4=bmsmi6009C72oNAJra&4qEm;vDKwve2v|7RH zB|v}x0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&Um`>pSCq znzAH7fB*pk1PBo5Dv-L)ZgK(y2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PB}!NQc+30s#U92oNAJf`Hrfj?e`;5FkKcVS(G-n|{45tfF)R z1PBlyK!5-N0t5&UAV7cs0RjXF5Fjv*z}-Uu^OUF(0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZpp-x^6-XQc1PBly5L4hIX4+$C?NY_D`y)F71PIg-NOi)BN`L?X0t5&UAV7e? zv;wcwR;Dll1nLRodZ9%oK!5-N0t5&UAVA=%K)xEl`UD6NAV7cs0RjXFj3kgpN=7aO z2rMU%m#bL81PBlyK!8Ah0oTR$?|}>m5FkK+009C7<`?j}d;T7)kN^P!1PBlyK!Cuc z0=~{n+E`@~xF>MCx4`=Z2oNAZfB=C~0;yCWaR?9~K!5-N0t5&UAV7cs0RjXFEF+MX zsdU0ZHE+VEDv1CA0t5&UAVA_-j+wTs z2@oJafB=Cz0_o2C-X=hR009C&3%GOO&o162K!5-N0t5&UAV7e?9)b5ggNz|SfB*pk z1PH_sa2;lhKFN{*0RjXF5FkK+009C72oNAZpsYYD8%{I=1PH7tkk>3*)dUC-AV7e? zcme@V>&)Y+$%p^}0t5&UAh4i7Ua)2*6Cgl<009C72oNAZfB*pk1PBlyK!5-N0tA*8 za1-vO+pl;61PBlyK!5-N0t5&UAV7cs0RjXF5FkK+Kq&z?^DiYS4gmrL2oNAZfB*pk z1f~}7^>OOX3xWFgKnAA;w$p1`kpKY#1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAJrGP7HrfjRC2oNAZfB*pk1PBlya9AK6Uc(9m2oNAZfB*pk1PBlyK%lyS zTMtzyHUR+w1PH_v@aHjJ&ty!1009C72oNAZfB*pk1PBlyK!5-N0tCtm+{%Xg5^ekP z+Difi2oUHlkh*6e0RjXF+!083-uE^E0t5&UxG&%=$on>ZmbeRDA9u47AV7cs0RjXF z5FkK+009C72oNAZfB*pktpw_xciXB8^AI3Fptpcy%e^~begXst5FkK+009C71{c@{ z&rf;;0t>hXA#n4gPJjRb0t5&UAV7cs0RjXF5a=h6`%P`;Lx`}RJCMFO;;59;$awI^2009C72oQ)S;B5M69g{Bs0t5)`5wII&kCHJ22oNAZ zfB*pk1PBlyK!5-N0t5&U_))+i#2;C_Nq_(W0t5(*B5)fe5qS_GK!5-N0t5&UAh4)F zTC{Fu6IejNN$3kSS&0M)5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RoQ% zT(|Q`$Fh(D9^MqPZIUKHfB*pk1PBlyK!5-N0t5&UAV8qGfcua(C$Br9-Q#tAMJGUj z009C72oNAZfB*pk1PBlyK!89P0XvMtv`V4`2oNAZfB*pk1PBlyKwumJcO5v_i+pZ1 zYY`woAcjDWk)|vO5FkK+009C72oNAZfB=DV0!}_FXDJc^0t5&UAV7cs0RjXF5FkK+ z009C72oNAZU_605UP>||K!5-N0t5&UAaFn+9~f&W0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!Cu+0&nxw<{JNbdZ`iu1PBlyK!5-N0t5&UAV7cs0Rn#uY=8gY{{#pSAV7cs z0RjXF5FkK+009C72oM-sAdQ`#>_P~0NE4-0tlo4IZBZL0RjXF5FkK+ z009C72oNAZfWS@xyS{fq8AgBr0RjXFTot%o4Pbo&1cC}o^Z?DE9gsEw0t5&UAV7cs z0RjXF6c%uNtuUx?g9zk7@{k4r0t5&UAV7cs0Rp26xZBm3ok?S+B`X312oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB=CL0^5motU`bQ0RjXF5FkK+ z009C72oNAZpn-tfuQfn61pxvC2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009DF1w7ItZ0jUWfB*pk1PBlyK!5-N0t5&UAVA=rz}vkA-X}nS009C72oNAZfB*pk z1PBlyK!5-N0tDg-xTj*gp2?U10RjXF5FkK+009C72oNAZfB*pkLkN5fk%c4(5FkK+ z009C72oNAZfB*pk1PBlyK!5;&00OuDIXcCCzm<^$2oNAZAg+MB*~RUh%n1-6K!Ctm zfso($)7fgRM}Po<+5)Ne1mY7QK!5-N0t5&Uh$P@r$4K3hD**xo2oM-az{v|Ebwe%$ z2oNAZfB*pk1eO;#aPighRsaYPAV7csfnowaf)t~iG*HNk04D8)vQ`&xZp`Xifcgm# zAV7cs0RjY~2sk4xN~fCUX|rg`{dltN@nTB}5FkK+KqLVNaUyj~t_0>4c%8R2l@TC7 zfB=Dk1pLJvs2NfrFp|JFQZjNOu(&|9Gd%O+{Zc*w0*wSxqj^k2fIv+F`y^`WicNq3 zfzJZY0{QIW6@mE$^86*LkN^P!1PBlyK!5-N0t5)GAmCui3SCws0RjXF5FkK+009C7 z2oNAZfB*pk1PHVh$Ze-H8vz0Y2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5O^-& zX{67M)*cb<2GwhKOZ5Z@5FkK+009C72oN9;Lmg;uF3fyf$g84{6K&J0RjX*3EV!t@`3;X0t5&UAV7csfhGcOc+-U0Bm@Xt z63Ca0=9^u+94SN1PBlyK!5;&`2^ZuW0B`WR1pCJ1PBnATfqI_=I*cR2oNAZfB*pk z1f~^ezvpjSN`(<1K!5-N0t5&UAV7cs0RjXF5FkK+009C7>I>xhQ<#AO0RjXF5FkK+ z009C72oNAZU^an|*$Pq*0RjXF5FkK+0D*!69&=F;Qz!xi2oNAZfB*pk1PBlyFuXt> zK0^r-AV7cs0RjXF5FkK+009C72oNAZfB*pka|yVvXRdy#riy@nCRJDkB|v}x0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7e?9)WGoAY%v+Xei*s%7)mcB0wOv zz!p1w*%R0+;2!FGrHms$fB*pk1PBlyK!5-N0t5&UAV7dXU4gA`c+n3FydGY|3Iqrc zAV7cs0RjXF{3PJB^-n6^BS2sY0lxs3XtN>-5FkK+009C72oNAZfB*pk1PBlyK!5-N z0-XikI?r$VfdyBIfG9u7S zz=JEBQJaVW0RjXF5FkJxv_KA>z2pfHAV7cs0RjXF5Fij!AjeEw)&vL;AV7cs0RjXF z5FkK+009C72m};xR&~IpNtplv0t5&UAV7cs0RjXF5FkK+KyiUoJfeUE2oNAZfB=CA z0v`Gvp-XZkK!5-N0t5&UAV7csfnfzchRsZ31iA{i^M6-qggnL+u>WDqzQ~FI0RjXF5FkK+009Dz1l;fKk&b1v3D|crTSwJHfWWE( zTie1@+v&_kfB*pk1PH_uaA$~E{gN#K0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk zD+{<&z{=fMI{^X&2oNAZfB*pk1PBlyK!Ct60k@a184w^qfB*pk1PBly&`rQG`)>3m zCP1L5z*aPxU<3#dAV7cs0RjXF5FkK+z(NA=3)QMr0t5&UAV7cs0RjXF5FkK+009C7 z2>cevAP5j3K!5-N0t6NiaM92LO;#cS0t5&UAV46Fz-ydTWlCT{f$dq%zF|HSvV6CI zjcGTPfdmK;AV7cs0RjXF5Lj9uEnU6h2@oJafB*pk1PBlyK%kgFE*5EDAScM|b25qm z0RjXF5Fjw0z&2k=r7KFM0|twigXJq-0t5&UAV7cs0RjYC3EWyuWF7(pz6dz5_XWgD z0;dJG(`$BI(X+}r;+vWP0RjXF5FkK+KoJ37?29l8LV&;u0`B>?LYLJ@fWRsO+bV_5 zS?AlFWvQx@z}9JA(~cyNMoLC51PBlyK!5-N0t5&UAV7cs0RjXFgb~PLG7XTZGcp1+ zNs0sr5FkK+009C72oNAZfB*pk1PBlaEnrV{==MpT009C7h8IY~XBaTS9581o6Cgl< z009C72oNAZfItrcJHUIWo09+m0t5&UAV7cs0RjXF5FkK+009Dh1l*mykGok35FkK+ z009C72oNAZU`~PeIm=Sjssc_XT($G+CP07y0RjXF5FkK+z%&ARno1NxfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfWQ(0wz;J(o`D1i5FkK+ z009Dp1>9A!FsN_@2wWG~u7?nT009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZptwLP9#KF71PJ^VaCwU@fdByl1PBlyK!8Aafh~Lm3LrqBjer~Q zwQ)8J0RjXF5V$DN|IVKm3$Qu?0t5&UAV7cs0RjXT5OB`b0!{u}qA%=UMaF%tSL60Z zW&{WjAn@|?0f7Jk0t5&UAV7cs0RjXF5FkK+0D)=(saj-#2@oJafB*pk1PBlyK!5-N z0t8A5Y$bzWvfz?pacjIAV7cs0RjXF5FkK+009Dv3V0aBqK#KJ0RjXF5FkK+009C72=o@X z^-jS21S$#G-BbxySONqH5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5(@7WgO~@L;^& z^B+vtZ~_Df5FkK+009C72;39!+?9JW-X}nS009Eu1^V98_j^9xAwYlt0RjXF5FoIi zfLk3b*mNZmAV7cs0RjXF5FkK+009C72oNAZ;75V<1e|-+$K9+12oNAZfB=Eg0;zPsWATQ)g6>!uMiU@F zpt*qGpF7E?+<)Pe)ScZoi~xb70)D+56*HayfnNfN0s#U92oNAZfB*pk1X>8x-YK42 z6k!em1PBlyK!5-N0t5&UAn;qj?MH111PBmVLZHGKtQA5o(F#QpAV7cs0RjXF5FkK+ z009C72oNAZfB*pkQ3P_7MCD0<009C7A`5&(PCjPt&KZo^w}G?X2F^`t1PD|X$d%&@ zzlwlQC#!T?odgIFAV7csfu9BP&u@G8ltAsQy;8;zAV7cs0RjXFytw4iMnHf70RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&U7+t{Sl4m>Q#Okwh)+0cG009C72oNAZfB*pk1PBly zK!5-N0t5&UAP`uGGk0t5&UAV7cs0RjXF5FkK+009Ew z1X8$+B}`y)0T&cc-r!>8IbKtYQXm2Z2oNAZprL?IyA82TMSuW-#{%z<7h6hz0D;c} z_CdBSK!5-N0t5&U_#)s*246tDBtYQh z#rXra0s;gG5FkK+009C72s{_?n1ts>Mi3xCfB=ER0-i>3n9K?U2oNAZfB*pk1PBly zK!5-N0t5&UXeZzd)1%hJj*1yiU>t!wPAW1XK!5-N0t5&UAV7e?1A+8lnMDK$5FkK+ z009C72oNAZfB*pk1PBxpaD$0rlmZbTFrq*jF)29_AV7cs0Rja22&6vKnw062P=evn_ z2oNAZfB*pk1PBlyK!8ARfsft^n4bUv0009C72oNAZfB*pk1PBlyK!5-N0t5&Q zEs%!JZn5Ov7OPje1PB}uaJu#ZEJFzpAV7cs0RjXF5LiUOiP4KRTA2j)3go>5jUzyS z0D&(8j!=C8@sa=m0t5&UAV7cs0RjXF5FkK+009C72oNAZfB=D)jR64y1PBlyK!5;& zwFL57<*Jqd0RjXF5FkK+0D)fu95r95FkK+009C72&^LTzDl9$BtU>bBmpKVN{+YU; z3H&Xv{r!Xg6Cgl<009C72oNAZfB*pk1PBlyK!5-N0t*PdFHoZr2@p6a;1|O|FvAJN z6YvD*cs-MGae-VsqJRX>2&{8CnLj;ewh!wNAV7csfmH?Uo2u1$_p+-cD=vX305V2m(6<@{Tcv z%qvjt1SNkT=G{tEMu5Ob0={XE)D5{1AV7cs0RjXF5FkK+zT}2@oJafB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009Cs3FMiIQ40YA1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+z$gOlDLzUkKmJZ009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RpiE97&GVFWC|xK!5-N0t5&UAV7csfrA2X2ge&u zfB*pk1PD|RNEKoVNq_)>I|7Hk0~{KBXF_iiAaGD19~^Hufu#i8p=POeE0zEO0t5&U zAV7dXD*-3Ww~{sw0RjXF5FkK+z(4};1LYzW0t5&U*eBp<={~0eqtbz~h7u?vkjjJ+ zrL(}+dHyHUJMZ%eki`TD5FkK+0D*b}zPi`5EgAWvWH7M^5FkKcCV@OtF=`<|fB*pk z1PBlyK!5-N0t5&UAV7cs0RjYu5=cX3vraOupj)TcDkVUG009C72oNAZfIxA9t$0KM z!wJ~45Uypl6LvjHZDH{V5FkK+KotR>>Z-5`N`L?X0t5)e60lbzR=;H1T)+*3nv+j8 zVVbHM#SkDsfIwY=t!8+)qpqnd_OJpr{9#)ov1S5}gEgZz5di`O2oNAZfB*pk1PBla zDd2>WkZqGR0RjXF5FkK+0D&b09PwYG&59&IfWR{WyRV)JSx$fe0RjXF5FkK+009C7 z2oNAZfWQa>d4we7K!5-N0t5&UAV7cs0RjXF5FkL{tibEp6|F~r009C72oNAZfIw$~ z)Omi>6Cgl<009C7E(&ZH*S9(W0t5(5AkgkELumpnB@iG$fB*pk1PBlyK!5-N0t5&U zAV7cs0Rq7UT(lFcVMWrt6^SAU0RpoL_}wsDN7X}s009C71`t^8E2pPe4Y1WofdByl z1PBlqPT+mGj3h*W009C72oNC9Kw!CjgAGhgL4W`O0t5&UAaGeAUydLE0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXb3AB66)uSvdBS3%v0RjXF92RhE zh{I%7AV7cs0RjXF5FkK+009C72oNAZfB=EY0=aU0;Rz5RK!5-N0t5&UAV7cs0RjXF z5FkK+z$F3Kl3n7nG64c51^hxPsVNo#0t5&UAV7cs0RjXF5Fijj;LjZ*IYh>nlk~Lk z%Y*_DAV7cs0RjXF5FqfEz>Nd}0t5&UAV7cs0RjZ#3;cPsD1J36AV7cs0RjZd3WPh! z!bh938-Qp82oP9Iz`?)88s4_tt?hJXBS3%v0RlS((#~;)5g4+_72<+@h=+?aYB4&6~NqbJpgs$eevu6#)VS z2oRWDz{l>18|?Gx#LZP&MFGDSE8+@GfB*pk1PBlyK!5-N0!;*RlUYncfB*pk1PBly zK!5-N0t5)u6>#*ruCM3>2oR_%@Lo5(=mbIuq)^#PmH+_)1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB=DN0-oA(LCuwnkz-1agl_%}Ia&0Rk%s zxQb_mE;p+&H=D^s1PBm_D{x_VhaFyV+b(kg1PBlyK!5-N0t5&UAV7dXQ-R!cHj@z` zK!5-N0t5&UAP_|0!r|&56-tu;0RjXF5FkK+009Ec1$@h+(1PBlyK!5-N z0t5&UAV7cs0RjXF{1&)@=p^8wA)Vm=Gws%KZc`H=Fq?ogf@bTedI%68K!5-N0t5&U zAP`kxi<-E+2@oJqQ^0Ysnz~{WAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0w)BV&TsYJMY0RjXF5FkK+009C72oNApN#Nhxfu>3`2upwf zfsq8#NXf{B0D%hv`NFzZB|v}x0Rlw?wjxmkAwYn@GXdvpJrlB=009E$1pIzI=VmPe z1ZoMq)e77*Ztgj`xd{*;K%l6=d(miu5g-sqAP34-s*?yz`Q>Pm-qbI{9cbzsn}GlU z0t5&U*d<^`$S$UMgO+WquUYXnRvGUTux;;iGKv5J0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!Cs?0^1;YNP_@@MglGtYXof?0t5&UAV7dXAb}JpSE&*pK!5-N z0t5&UAV7cs0RjXF5Fijp;5AULQYAou009C72oNA}M_{t2YNk77dYb@&`vQ~w8n|Dm zB?Jf%ATXxDHfCC~no__{{VCh3sCojvDb%xFH?qfktlN8)6Cgl<009Dr1>8C1Fqsty z5FkK+0D(aT>=GKZG14MHfIu;UR4kG}1pX3moYJO1fB*pk1PBlyK!Ct~fn`qyb_DtU zMq$a~0uDngz5ytoKtO@_fH_N<009C72oNAZfWQ%fbY!rx1S$)p%JGF?Twq(ge&vrL zusAV6Shfo`}HV**;X9b+sc2>@M1PDwbkSD1_83YIrAV7e?i~Awi2nY}$ zK!5;&S^}w7U~vf$7+PQ(Iy=b`AV7csfn^0gmaSaj1g;49mvcpL^>t@H33le%R^M*b zPk;ac0t5&UAV7cs0RmwJZecQ&C;sI1PBm#B#<919AH@*AO|TB zAV6Rxfvrrj?i^LdQ4|6M2oNC9S-{TM&J8d<0RjYy3FKmt1R_9y009C72oNAJy?|pJ zf3!IL@pJnhxAs$-kpKY#1PBlyK!5-N0t5&UAV7cs0RjXF5SU3|o2eMJ5FkK+009C7 z2oNAZV0M8#dx7dBK!5-N0t5&UAV7dXIe}ZbP$Cf^K!5-N0t5&UAV7cs0RjZB2)tie z*}4P>5FkK+009C72oNAZfB*pk1pX56d?cF!0RjXF5SUNE?f&QMsfq{?AV7cs0RjXj z6?mPrE@crQK!Cu20xpmquqiXAWEbVk-BlX_0;3D~ra5|tH2NXjUOWfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBoLTfjNIe@pm30RjXF5FkK+009C7P6)i8SjQ>^2oNAZfB*pk1PBly5M5v^TtF%u zPdEYu2oNAZfB*pk1PBlyK!5;&83b-K6rlzJ1PBlyK!5-N0t5&UAV6Sr0oR(3-l50& zy*^%SDFFfm2oNAZfB*pk2>}5D1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pkWdyb|VMHN7fB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAJus|L- zH>nXIK!5-N0tB82q$kTQCP07y0RjXF1QvJ;oO|HZQ{ddCP9Ts#$LB}A2V#;c0RjXF z5U4AV>V_Ac009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7csf%ycS**ITMRrG_v_QQML zB0zw^8UksJGF3@{Kz)JN`cs&J009C72oNAZfB*pk1PBlyK%lmOojkRL#V0_3z@P$o z(7dEYfB=Eh0{QfsRwO`x009C72oNAZfWW!}E+SjE_bMkqfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZ;H1EIay_dNAV7cs0RjXF5Li_puUfde34{}P50|lo z2@tp<;NSceJ?j!6@KnGx%uk67AV7e?oC4dNWvPk)0RjXF%p>5r6!Y{_B?Jf%AV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBly@Jk?3AV7cs0RjXF5Fl_}VC_q8Kdu9cK!5-N z0t5)GA+Yv`rZsx3N&*B35U4AV>V_XH`mIztxl|x=!V47JsKQHJFQCp2K^?l@84(B& zAV7cs0RjXF5FoHqz#*HRP=*m8K!89gfjavXOJyex0RjXF5FkK+009C7+6j0dTRUqr z5gqF0t5&UAV7cs0RjXF5FkK+009C72oNAZfWY_y&dV6TM=~TZ ztUwwzGl>x(Kwxx%G1l*5hl13_n z009C72oP96;I=@GN+dvFZGpUY`Kl*CfB*pk1fC1LJs)BOffWSoAy}cy%hmY*CT(v! literal 0 HcmV?d00001 diff --git a/test/data/serialization/3.0/utils.BloomFilter1000.bin b/test/data/serialization/3.0/utils.BloomFilter1000.bin new file mode 100644 index 0000000000000000000000000000000000000000..cde99c9860913beecb4469e7df30ee4655e7e805 GIT binary patch literal 2500016 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

gp(T){FtAAw_0Mzc5y%@ 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 keys) + @Test + @Ignore + public void testManyRandom() + { + testManyRandom(FilterTestHelper.randomKeys(), false); + + testManyRandom(FilterTestHelper.randomKeys(), true); + } + + private static void testManyRandom(Iterator keys, boolean oldBfHashOrder) { int MAX_HASH_COUNT = 128; - Set hashes = new HashSet(); + Set hashes = new HashSet<>(); long collisions = 0; while (keys.hasNext()) { hashes.clear(); FilterKey buf = FilterTestHelper.wrap(keys.next()); - BloomFilter bf = (BloomFilter) FilterFactory.getFilter(10, 1, false); + BloomFilter bf = (BloomFilter) FilterFactory.getFilter(10, 1, false, oldBfHashOrder); for (long hashIndex : bf.getHashBuckets(buf, MAX_HASH_COUNT, 1024 * 1024)) { hashes.add(hashIndex); @@ -143,28 +175,48 @@ public class BloomFilterTest collisions += (MAX_HASH_COUNT - hashes.size()); bf.close(); } - assert collisions <= 100; - } - - @Test - public void testManyRandom() - { - testManyHashes(FilterTestHelper.randomKeys()); + Assert.assertTrue("collisions=" + collisions, collisions <= 100); } @Test(expected = UnsupportedOperationException.class) public void testOffHeapException() { - long numKeys = (Integer.MAX_VALUE * 64) + 1; // approx 128 Billion - FilterFactory.getFilter(numKeys, 0.01d, true); + long numKeys = ((long)Integer.MAX_VALUE) * 64L + 1L; // approx 128 Billion + FilterFactory.getFilter(numKeys, 0.01d, true, true).close(); } @Test - public void compareCachedKey() + public void compareCachedKeyOldHashOrder() { - try (BloomFilter bf1 = (BloomFilter) FilterFactory.getFilter(FilterTestHelper.ELEMENTS / 2, FilterTestHelper.MAX_FAILURE_RATE, false); - BloomFilter bf2 = (BloomFilter) FilterFactory.getFilter(FilterTestHelper.ELEMENTS / 2, FilterTestHelper.MAX_FAILURE_RATE, false); - BloomFilter bf3 = (BloomFilter) FilterFactory.getFilter(FilterTestHelper.ELEMENTS / 2, FilterTestHelper.MAX_FAILURE_RATE, false)) + BloomFilter bf1 = (BloomFilter) FilterFactory.getFilter(FilterTestHelper.ELEMENTS / 2, FilterTestHelper.MAX_FAILURE_RATE, false, true); + BloomFilter bf2 = (BloomFilter) FilterFactory.getFilter(FilterTestHelper.ELEMENTS / 2, FilterTestHelper.MAX_FAILURE_RATE, false, true); + BloomFilter bf3 = (BloomFilter) FilterFactory.getFilter(FilterTestHelper.ELEMENTS / 2, FilterTestHelper.MAX_FAILURE_RATE, false, true); + + RandomStringGenerator gen1 = new KeyGenerator.RandomStringGenerator(new Random().nextInt(), FilterTestHelper.ELEMENTS); + + // make sure all bitsets are empty. + BitSetTest.compare(bf1.bitset, bf2.bitset); + BitSetTest.compare(bf1.bitset, bf3.bitset); + + while (gen1.hasNext()) + { + ByteBuffer key = gen1.next(); + FilterKey cached = FilterTestHelper.wrapCached(key); + bf1.add(FilterTestHelper.wrap(key)); + bf2.add(cached); + bf3.add(cached); + } + + BitSetTest.compare(bf1.bitset, bf2.bitset); + BitSetTest.compare(bf1.bitset, bf3.bitset); + } + + @Test + public void compareCachedKeyNewHashOrder() + { + try (BloomFilter bf1 = (BloomFilter) FilterFactory.getFilter(FilterTestHelper.ELEMENTS / 2, FilterTestHelper.MAX_FAILURE_RATE, false, false); + BloomFilter bf2 = (BloomFilter) FilterFactory.getFilter(FilterTestHelper.ELEMENTS / 2, FilterTestHelper.MAX_FAILURE_RATE, false, false); + BloomFilter bf3 = (BloomFilter) FilterFactory.getFilter(FilterTestHelper.ELEMENTS / 2, FilterTestHelper.MAX_FAILURE_RATE, false, false)) { RandomStringGenerator gen1 = new KeyGenerator.RandomStringGenerator(new Random().nextInt(), FilterTestHelper.ELEMENTS); @@ -189,11 +241,17 @@ public class BloomFilterTest @Test @Ignore public void testHugeBFSerialization() throws IOException + { + hugeBFSerialization(false); + hugeBFSerialization(true); + } + + static void hugeBFSerialization(boolean oldBfHashOrder) throws IOException { ByteBuffer test = ByteBuffer.wrap(new byte[] {0, 1}); File file = FileUtils.createTempFile("bloomFilterTest-", ".dat"); - BloomFilter filter = (BloomFilter) FilterFactory.getFilter(((long) Integer.MAX_VALUE / 8) + 1, 0.01d, true); + BloomFilter filter = (BloomFilter) FilterFactory.getFilter(((long) Integer.MAX_VALUE / 8) + 1, 0.01d, true, oldBfHashOrder); filter.add(FilterTestHelper.wrap(test)); DataOutputStreamPlus out = new BufferedDataOutputStreamPlus(new FileOutputStream(file)); FilterFactory.serialize(filter, out); @@ -202,7 +260,7 @@ public class BloomFilterTest filter.close(); DataInputStream in = new DataInputStream(new FileInputStream(file)); - BloomFilter filter2 = (BloomFilter) FilterFactory.deserialize(in, true); + BloomFilter filter2 = (BloomFilter) FilterFactory.deserialize(in, true, oldBfHashOrder); Assert.assertTrue(filter2.isPresent(FilterTestHelper.wrap(test))); FileUtils.closeQuietly(in); filter2.close(); diff --git a/test/unit/org/apache/cassandra/utils/SerializationsTest.java b/test/unit/org/apache/cassandra/utils/SerializationsTest.java index f3809b37b8..c50f40035a 100644 --- a/test/unit/org/apache/cassandra/utils/SerializationsTest.java +++ b/test/unit/org/apache/cassandra/utils/SerializationsTest.java @@ -24,42 +24,176 @@ import java.io.IOException; import org.junit.Assert; import org.junit.Test; import org.apache.cassandra.AbstractSerializationsTester; +import org.apache.cassandra.db.DecoratedKey; +import org.apache.cassandra.db.marshal.Int32Type; import org.apache.cassandra.dht.IPartitioner; import org.apache.cassandra.io.util.DataInputPlus.DataInputStreamPlus; import org.apache.cassandra.io.util.DataOutputStreamPlus; import org.apache.cassandra.service.StorageService; +import org.apache.cassandra.dht.Murmur3Partitioner; + +import java.io.File; +import java.io.FileInputStream; public class SerializationsTest extends AbstractSerializationsTester { - private void testBloomFilterWrite(boolean offheap) throws IOException + private static void testBloomFilterWrite(boolean offheap, boolean oldBfHashOrder) throws IOException { IPartitioner partitioner = StorageService.getPartitioner(); - try (IFilter bf = FilterFactory.getFilter(1000000, 0.0001, offheap)) + try (IFilter bf = FilterFactory.getFilter(1000000, 0.0001, offheap, oldBfHashOrder)) { for (int i = 0; i < 100; i++) bf.add(partitioner.decorateKey(partitioner.getTokenFactory().toByteArray(partitioner.getRandomToken()))); - try (DataOutputStreamPlus out = getOutput("utils.BloomFilter.bin")) + try (DataOutputStreamPlus out = getOutput(oldBfHashOrder ? "2.1" : "3.0", "utils.BloomFilter.bin")) + { + FilterFactory.serialize(bf, out); + } + } + } + + private static void testBloomFilterWrite1000(boolean offheap, boolean oldBfHashOrder) throws IOException + { + IPartitioner partitioner = StorageService.getPartitioner(); + try (IFilter bf = FilterFactory.getFilter(1000000, 0.0001, offheap, oldBfHashOrder)) + { + for (int i = 0; i < 1000; i++) + bf.add(partitioner.decorateKey(Int32Type.instance.decompose(i))); + try (DataOutputStreamPlus out = getOutput(oldBfHashOrder ? "2.1" : "3.0", "utils.BloomFilter1000.bin")) { FilterFactory.serialize(bf, out); } } } + @Test + public void testBloomFilterRead1000() throws IOException + { + if (EXECUTE_WRITES) + { + testBloomFilterWrite1000(true, false); + testBloomFilterWrite1000(true, true); + } + + IPartitioner partitioner = StorageService.getPartitioner(); + try (DataInputStream in = getInput("3.0", "utils.BloomFilter1000.bin"); + IFilter filter = FilterFactory.deserialize(in, true, false)) + { + boolean present; + for (int i = 0 ; i < 1000 ; i++) + { + present = filter.isPresent(partitioner.decorateKey(Int32Type.instance.decompose(i))); + Assert.assertTrue(present); + } + for (int i = 1000 ; i < 2000 ; i++) + { + present = filter.isPresent(partitioner.decorateKey(Int32Type.instance.decompose(i))); + Assert.assertFalse(present); + } + } + + try (DataInputStream in = getInput("2.1", "utils.BloomFilter1000.bin"); + IFilter filter = FilterFactory.deserialize(in, true, true)) + { + boolean present; + for (int i = 0 ; i < 1000 ; i++) + { + present = filter.isPresent(partitioner.decorateKey(Int32Type.instance.decompose(i))); + Assert.assertTrue(present); + } + for (int i = 1000 ; i < 2000 ; i++) + { + present = filter.isPresent(partitioner.decorateKey(Int32Type.instance.decompose(i))); + Assert.assertFalse(present); + } + } + + // eh - reading version version 'ka' (2.1) with 3.0 BloomFilter + int falsePositive = 0; + int falseNegative = 0; + try (DataInputStream in = getInput("2.1", "utils.BloomFilter1000.bin"); + IFilter filter = FilterFactory.deserialize(in, true, false)) + { + boolean present; + for (int i = 0 ; i < 1000 ; i++) + { + present = filter.isPresent(partitioner.decorateKey(Int32Type.instance.decompose(i))); + if (!present) + falseNegative ++; + } + for (int i = 1000 ; i < 2000 ; i++) + { + present = filter.isPresent(partitioner.decorateKey(Int32Type.instance.decompose(i))); + if (present) + falsePositive ++; + } + } + Assert.assertEquals(1000, falseNegative); + Assert.assertEquals(0, falsePositive); + } + + @Test + public void testBloomFilterTable() throws Exception + { + testBloomFilterTable("test/data/bloom-filter/ka/foo/foo-atable-ka-1-Filter.db", true); + testBloomFilterTable("test/data/bloom-filter/la/foo/la-1-big-Filter.db", false); + } + + private static void testBloomFilterTable(String file, boolean oldBfHashOrder) throws Exception + { + Murmur3Partitioner partitioner = new Murmur3Partitioner(); + + try (DataInputStream in = new DataInputStream(new FileInputStream(new File(file))); + IFilter filter = FilterFactory.deserialize(in, true, oldBfHashOrder)) + { + for (int i = 1; i <= 10; i++) + { + DecoratedKey decoratedKey = partitioner.decorateKey(Int32Type.instance.decompose(i)); + boolean present = filter.isPresent(decoratedKey); + Assert.assertTrue(present); + } + + int positives = 0; + for (int i = 11; i <= 1000010; i++) + { + DecoratedKey decoratedKey = partitioner.decorateKey(Int32Type.instance.decompose(i)); + boolean present = filter.isPresent(decoratedKey); + if (present) + positives++; + } + double fpr = positives; + fpr /= 1000000; + Assert.assertTrue(fpr <= 0.011d); + } + } + @Test public void testBloomFilterReadMURMUR3() throws IOException { if (EXECUTE_WRITES) - testBloomFilterWrite(true); + testBloomFilterWrite(true, true); - try (DataInputStream in = getInput("utils.BloomFilter.bin"); - IFilter filter = FilterFactory.deserialize(in, true)) + try (DataInputStream in = getInput("3.0", "utils.BloomFilter.bin"); + IFilter filter = FilterFactory.deserialize(in, true, true)) { Assert.assertNotNull(filter); } } - private void testEstimatedHistogramWrite() throws IOException + @Test + public void testBloomFilterReadMURMUR3pre30() throws IOException + { + if (EXECUTE_WRITES) + testBloomFilterWrite(true, false); + + try (DataInputStream in = getInput("2.1", "utils.BloomFilter.bin"); + IFilter filter = FilterFactory.deserialize(in, true, false)) + { + Assert.assertNotNull(filter); + } + } + + private static void testEstimatedHistogramWrite() throws IOException { EstimatedHistogram hist0 = new EstimatedHistogram(); EstimatedHistogram hist1 = new EstimatedHistogram(5000);