fix(bug): dashtable split crashes when moving items from the old segment (#318)

fix(bug): dashtable split crashes when moving items from the old segment.

Segment's Insert function uses an opportunistic heuristic that chose a bucket with smaller items among two available.
This creates a problem with split that goes from a smaller bucket to the biggest one and moves items to the new segment.
In rare case, the heurstic fills up a the next bucket with items that could reside in earlier buckets and then that bucket
does not have space for its own items. Eventually, items that had enough space in the old segment do not find space in the new one

The fix is to adopt a conservative approach during split and try to use a home bucket first. Home bucket is usually a smaller one.
This approach should be optimal because Split starts with smaller buckets first (can be proven iteratively).

Signed-off-by: Roman Gershman <roman@dragonflydb.io>

Signed-off-by: Roman Gershman <roman@dragonflydb.io>
This commit is contained in:
Roman Gershman 2022-09-20 10:57:41 -07:00 committed by GitHub
parent 751bcb9aec
commit 082ac36ac1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 803 additions and 10 deletions

View File

@ -11,7 +11,7 @@ cxx_test(dfly_core_test dfly_core LABELS DFLY)
cxx_test(compact_object_test dfly_core LABELS DFLY) cxx_test(compact_object_test dfly_core LABELS DFLY)
cxx_test(extent_tree_test dfly_core LABELS DFLY) cxx_test(extent_tree_test dfly_core LABELS DFLY)
cxx_test(external_alloc_test dfly_core LABELS DFLY) cxx_test(external_alloc_test dfly_core LABELS DFLY)
cxx_test(dash_test dfly_core LABELS DFLY) cxx_test(dash_test dfly_core file DATA testdata/ids.txt LABELS DFLY)
cxx_test(interpreter_test dfly_core LABELS DFLY) cxx_test(interpreter_test dfly_core LABELS DFLY)
cxx_test(json_test dfly_core TRDP::jsoncons LABELS DFLY) cxx_test(json_test dfly_core TRDP::jsoncons LABELS DFLY)
cxx_test(string_set_test dfly_core LABELS DFLY) cxx_test(string_set_test dfly_core LABELS DFLY)

View File

@ -492,7 +492,12 @@ template <typename _Key, typename _Value, typename Policy = DefaultSegmentPolicy
// Returns valid iterator if succeeded or invalid if not (it's full). // Returns valid iterator if succeeded or invalid if not (it's full).
// Requires: key should be not present in the segment. // Requires: key should be not present in the segment.
template <typename U, typename V> Iterator InsertUniq(U&& key, V&& value, Hash_t key_hash); // if spread is true, tries to spread the load between neighbour and home buckets,
// otherwise chooses home bucket first.
// TODO: I am actually not sure if spread optimization is helpful. Worth checking
// whether we get higher occupancy rates when using it.
template <typename U, typename V>
Iterator InsertUniq(U&& key, V&& value, Hash_t key_hash, bool spread);
// capture version change in case of insert. // capture version change in case of insert.
// Returns ids of buckets whose version would cross ver_threshold upon insertion of key_hash // Returns ids of buckets whose version would cross ver_threshold upon insertion of key_hash
@ -526,7 +531,8 @@ template <typename _Key, typename _Value, typename Policy = DefaultSegmentPolicy
} }
// Bumps up this entry making it more "important" for the eviction policy. // Bumps up this entry making it more "important" for the eviction policy.
template<typename BumpPolicy> Iterator BumpUp(uint8_t bid, SlotId slot, Hash_t key_hash, const BumpPolicy& ev); template <typename BumpPolicy>
Iterator BumpUp(uint8_t bid, SlotId slot, Hash_t key_hash, const BumpPolicy& ev);
// Tries to move stash entries back to their normal buckets (exact or neighbour). // Tries to move stash entries back to their normal buckets (exact or neighbour).
// Returns number of entries that succeeded to unload. // Returns number of entries that succeeded to unload.
@ -1048,7 +1054,7 @@ auto Segment<Key, Value, Policy>::Insert(U&& key, V&& value, Hash_t key_hash, Pr
return std::make_pair(it, false); /* duplicate insert*/ return std::make_pair(it, false); /* duplicate insert*/
} }
it = InsertUniq(std::forward<U>(key), std::forward<V>(value), key_hash); it = InsertUniq(std::forward<U>(key), std::forward<V>(value), key_hash, true);
return std::make_pair(it, it.found()); return std::make_pair(it, it.found());
} }
@ -1181,8 +1187,13 @@ void Segment<Key, Value, Policy>::Split(HFunc&& hfn, Segment* dest_right) {
invalid_mask |= (1u << slot); invalid_mask |= (1u << slot);
auto it = dest_right->InsertUniq(std::forward<Key_t>(key), auto it = dest_right->InsertUniq(std::forward<Key_t>(key),
std::forward<Value_t>(Value(i, slot)), hash); std::forward<Value_t>(Value(i, slot)), hash, false);
// we move items residing in a regular bucket to a new segment.
// I do not see a reason why it might overflow to a stash bucket.
assert(it.index < kNumBuckets);
(void)it; (void)it;
if constexpr (USE_VERSION) { if constexpr (USE_VERSION) {
// Maintaining consistent versioning. // Maintaining consistent versioning.
uint64_t ver = bucket_[i].GetVersion(); uint64_t ver = bucket_[i].GetVersion();
@ -1218,7 +1229,7 @@ void Segment<Key, Value, Policy>::Split(HFunc&& hfn, Segment* dest_right) {
invalid_mask |= (1u << slot); invalid_mask |= (1u << slot);
auto it = dest_right->InsertUniq(std::forward<Key_t>(Key(bid, slot)), auto it = dest_right->InsertUniq(std::forward<Key_t>(Key(bid, slot)),
std::forward<Value_t>(Value(bid, slot)), hash); std::forward<Value_t>(Value(bid, slot)), hash, false);
(void)it; (void)it;
assert(it.index != kNanBid); assert(it.index != kNanBid);
@ -1283,7 +1294,8 @@ bool Segment<Key, Value, Policy>::CheckIfMovesToOther(bool own_items, unsigned f
template <typename Key, typename Value, typename Policy> template <typename Key, typename Value, typename Policy>
template <typename U, typename V> template <typename U, typename V>
auto Segment<Key, Value, Policy>::InsertUniq(U&& key, V&& value, Hash_t key_hash) -> Iterator { auto Segment<Key, Value, Policy>::InsertUniq(U&& key, V&& value, Hash_t key_hash, bool spread)
-> Iterator {
const uint8_t bid = BucketIndex(key_hash); const uint8_t bid = BucketIndex(key_hash);
const uint8_t nid = NextBid(bid); const uint8_t nid = NextBid(bid);
@ -1295,7 +1307,7 @@ auto Segment<Key, Value, Policy>::InsertUniq(U&& key, V&& value, Hash_t key_hash
unsigned ts = target.Size(), ns = neighbor.Size(); unsigned ts = target.Size(), ns = neighbor.Size();
bool probe = false; bool probe = false;
if (ts > ns) { if (spread && ts > ns) {
insert_first = &neighbor; insert_first = &neighbor;
probe = true; probe = true;
} }
@ -1305,6 +1317,12 @@ auto Segment<Key, Value, Policy>::InsertUniq(U&& key, V&& value, Hash_t key_hash
insert_first->Insert(slot, std::forward<U>(key), std::forward<V>(value), meta_hash, probe); insert_first->Insert(slot, std::forward<U>(key), std::forward<V>(value), meta_hash, probe);
return Iterator{uint8_t(insert_first - bucket_), uint8_t(slot)}; return Iterator{uint8_t(insert_first - bucket_), uint8_t(slot)};
} else if (!spread) {
int slot = neighbor.FindEmptySlot();
if (slot >= 0) {
neighbor.Insert(slot, std::forward<U>(key), std::forward<V>(value), meta_hash, true);
return Iterator{nid, uint8_t(slot)};
}
} }
int displace_index = MoveToOther(true, nid, NextBid(nid)); int displace_index = MoveToOther(true, nid, NextBid(nid));
@ -1551,7 +1569,8 @@ auto Segment<Key, Value, Policy>::FindValidStartingFrom(unsigned bid, unsigned s
template <typename Key, typename Value, typename Policy> template <typename Key, typename Value, typename Policy>
template <typename BumpPolicy> template <typename BumpPolicy>
auto Segment<Key, Value, Policy>::BumpUp(uint8_t bid, SlotId slot, Hash_t key_hash, const BumpPolicy& bp) -> Iterator { auto Segment<Key, Value, Policy>::BumpUp(uint8_t bid, SlotId slot, Hash_t key_hash,
const BumpPolicy& bp) -> Iterator {
auto& from = bucket_[bid]; auto& from = bucket_[bid];
uint8_t target_bid = BucketIndex(key_hash); uint8_t target_bid = BucketIndex(key_hash);

View File

@ -16,6 +16,8 @@
#include "base/hash.h" #include "base/hash.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/zipf_gen.h" #include "base/zipf_gen.h"
#include "io/file.h"
#include "io/line_reader.h"
extern "C" { extern "C" {
#include "redis/dict.h" #include "redis/dict.h"
@ -796,6 +798,32 @@ TEST_F(DashTest, Sds) {
// dt.Insert(std::string_view{"bar"}, 1); // dt.Insert(std::string_view{"bar"}, 1);
} }
struct BlankPolicy : public BasicDashPolicy {
static uint64_t HashFn(uint64_t v) {
return v;
}
};
// The bug was that for very rare cases when during segment splitting we move all the items
// into a new segment, not every item finds a place.
TEST_F(DashTest, SplitBug) {
DashTable<uint64_t, uint64_t, BlankPolicy> table;
io::ReadonlyFileOrError fl_err =
io::OpenRead(base::ProgramRunfile("testdata/ids.txt"), io::ReadonlyFile::Options{});
CHECK(fl_err);
io::FileSource fs(std::move(*fl_err));
io::LineReader lr(&fs, DO_NOT_TAKE_OWNERSHIP);
string_view line;
uint64_t val;
while (lr.Next(&line)) {
CHECK(absl::SimpleHexAtoi(line, &val));
table.Insert(val, 0);
}
EXPECT_EQ(746, table.size());
}
/** /**
______ _ _ _ _______ _ ______ _ _ _ _______ _
| ____| (_) | | (_) |__ __| | | | ____| (_) | | (_) |__ __| | |

746
src/core/testdata/ids.txt vendored Normal file
View File

@ -0,0 +1,746 @@
a2e24b073cf57fa4
a2f1bbc7fc9600fb
a2f3684310f1e895
a2e8d1df84366075
a2e997d4134f4046
a2f9824557d420ba
a2e2d9d90b3b1841
a2f6f8031af068b8
a2ea89709696b08f
a2e7bf0f03267014
a2e3d46c5893efbb
a2e1c01c5e68c7dd
a2eaea6601b33fb0
a2e250ca959bc7f4
a2eb5e11044768fa
a2e3731bc400185c
a2f82888c8f868e2
a2eb9bea7247c808
a2f97d0065a380fd
a2f6940fd277d8f8
a2e638b5fd214061
a2f3b77dd4fdd85b
a2f61c0bb34320c4
a2e6aa99dfee1854
a2e9e267998b81e0
a2e7d6c50702811b
a2eacadee3e49971
a2f71490c5881072
a2eba6f3148c8926
a2eeb82230b8a96c
a2fa11351697219a
a2e470f5bdb82146
a2e989a0a3add110
a2f4e47496465138
a2fbf81d9eb3a12c
a2fb473cb87281f6
a2fac75352db492c
a2eecc5dd94671f1
a2e977499664a15d
a2ea0c28000911e2
a2f5fefbfa752226
a2fb862ad9362abf
a2e0a3a0629953fe
a2ebfbaa189813ac
a2f40ead50f6aad5
a2ffc642cc7e42d5
a2e7a35701176a31
a2f82c2ca98972e6
a2e1209b19f0da73
a2fb1a51b2cba2d9
a2fa434b183cbaef
a2e5bffa40a61a4f
a2e80f94a2033249
a2f768f7f5af23d3
a2ef76f25e1093df
a2e97b60a53f7bdb
a2f7ec84b9611387
a2f0f0a971a20be1
a2e3b72061eba3c1
a2e0e46833620bca
a2ee9ee8643b0bbd
a2f2e57deca70bb7
a2e56aa5a32fa3b3
a2e3337430edeb97
a2e8942657a43b5d
a2e10aadbef4c3f6
a2f7d759e4d30342
a2eb04df10f92b42
a2e17f2873509b6a
a2fc6e0b74e7835d
a2ea17280ed7f4e6
a2e3b163ddaf946d
a2e6bdef56ddb400
a2fdeabc7dd94c82
a2ec11e354bf7c34
a2efb83055c41405
a2f26430a428e409
a2f56566dbd2acbe
a2f21394b7be6c47
a2e18c40653fcc3a
a2f06a8ff1219473
a2f7e80c04eea4f9
a2f980638e0a4456
a2e57ee2be853440
a2e984e074002dee
a2f88db3a2c0dd3e
a2fc31867ca29d4b
a2f36b82e35cbd66
a2e11da0dbf24daf
a2e3145034660591
a2f7f1db168655d8
a2ea4d022ac46594
a2f4f617d700de11
a2e813b0d2542685
a2ebf14ecc74bef1
a2fa3e1591df7e26
a2f49650186be648
a2ee03aeb6d32eaf
a2fd7506c5f2865e
a2f204c1f965e6e4
a2ffd3f0ac6bdea3
a2f4bd6745756e66
a2f79471989e360b
a2eec66780ad9655
a2eb39c6c7134699
a2f482d83d8596bb
a2eca4aa0f2a0efc
a2f7c2719d8e4e47
a2fd49654604ee3c
a2e98788ed00ae99
a2efb147b11097e8
a2efe633d1a6f700
a2ebbde450f1cf39
a2f5b5b98a374742
a2f2af0b92d60f3b
a2fd96138c4b8785
a2e580f21c661f51
a2e78eecf23ccf14
a2fe52c33d66a71e
a2f934e1f2160f19
a2fcf00ca8c5c7fb
a2fecfe355d0ff3f
a2f1f20d7a248f61
a2fde1f40d1557ad
a2eaa243acac2064
a2e1a3390cd0e847
a2e1b4444a3e9075
a2f8e0f41a8cd8e6
a2ecbe972c3fd818
a2eae4fe970398c0
a2f95816742380c1
a2e4b575972a9031
a2eb15f66232b0f7
a2e3a74c018398ac
a2eab2edf417d048
a2e0440a262100cd
a2e7a4be7e0eb120
a2f1c1a1a6ba79ea
a2e02a14348e412d
a2e646e5f49641a4
a2fe6a1116d7490f
a2eccebbc172d926
a2ef35ccb31659b6
a2f6a08a2a8169ee
a2ebed8c7ba6f9fe
a2fc1bda0dcd9110
a2fbf857cf11ba0e
a2fc69f892bd32b5
a2e64615716f2296
a2e015b79319ca99
a2f7ead8ef9a5b24
a2ea0807e5433370
a2e4bba27feada4e
a2faa731689512a9
a2e5c057e3a54a66
a2eebeb3f91db2e1
a2ed34fb7842d2a3
a2fd5cb3baf74a09
a2e68b11e41c129f
a2edfcc93f47aad1
a2e4bc94b914aaf9
a2ffed576e3de3af
a2f427529e103b56
a2f730db92d69ba5
a2fa74d0015873df
a2f651345a345b20
a2ff3f79b100f360
a2fff6c8cd44239b
a2f38e5e2b8e5ba4
a2ea7cf369cb23fd
a2e09ca087e16b0a
a2f92a7f9076b32b
a2f740c5c746a3ac
a2e79530d5b8732f
a2e96e0f5f6593f4
a2fd47a4b7d9c3b9
a2f496d67891db58
a2e4e801c29b7b3d
a2f2c7a0e276841b
a2fa105c331874e9
a2f16f1aeedb347c
a2fa7762c5da2c2e
a2e671a0656e0cef
a2f3a6aac8066ce0
a2fae23544e16cca
a2f98c94bcdac46f
a2e8c7054839ec68
a2e15d7ed7be94ba
a2f60cd929b9fcc9
a2fc5b87f07e7cb6
a2f9d6ca31d22c01
a2f0a2b020297cf7
a2fc5fad51156d32
a2f131243676452a
a2e96bb1b43eed98
a2f25886d05715d0
a2e543f6079035ce
a2e93a4f4e92bdc1
a2fc3e6a34fa65bf
a2f5ef7570c03d56
a2e96d854c086e09
a2f5913bc1c2d6d0
a2fb4c3bf5fc1e8a
a2f72380e8706e44
a2f9670b15094e28
a2f1ac1ce0a0fefb
a2e8a1050a5ccf7f
a2ed52457a46d6ab
a2fc970329f2f689
a2e45d6cceced660
a2ff5adb32a1b6ed
a2e106993f46c653
a2fc27b50449aec2
a2e4d60ff6f0ae72
a2fd82c1befd06a9
a2ec1038472e6e87
a2e539611677de98
a2eab474e7b85e55
a2fd37c0aa996e2b
a2ea68b22962cf99
a2f81a14b7870719
a2ea048a126e4714
a2ec0ba843a1efe8
a2e61c86ef796791
a2e34e57600227b2
a2f6f7269c8e1f02
a2e3d5f020635723
a2f35a421d587f7c
a2e5ab56cfab67e7
a2ec0c3570d0bf8d
a2fcddf06fd84fa0
a2f868cbcaa8afd0
a2e562ef52980fcb
a2f43e70872b2f08
a2e3607908c3c0b1
a2fdc1f4f8eae874
a2f19564a576486a
a2e2607351536083
a2edf68234e71899
a2fb8e1d0a7f28f8
a2f19a5f9f01d0a5
a2e5b29bf006d832
a2ef5edd44c788f4
a2e43d4ba691784a
a2ea47046f8b0035
a2f3d6d5ab0b303a
a2e32021129a79ab
a2e55b853fe259a4
a2e9025193fe99ef
a2fc1f0dafbdc977
a2e3b2e42592e101
a2f8cdd05a1e5910
a2e145c888ad41c0
a2e08d7bc2bfc189
a2ee8ecf6cb2d9e9
a2e88dfb118b41a4
a2f90d672b30e9fd
a2fb9c54a6a2811f
a2f42048ca7e0943
a2f920472784018a
a2ecbf270467a911
a2ff7d158d3bd916
a2e531b5dcd3c2db
a2e80c2217186a30
a2f523fbc87f5a59
a2f45dd69efccaf0
a2fccf99ff9f8200
a2e855addee38af0
a2f0b87b8efe7a6c
a2e1a3eb966b4299
a2e1dcaa3a1cf231
a2fa3a9b65f5cb98
a2f5c40068b92bec
a2ea24325612d3d4
a2e2327bb939039c
a2ff8501a9d03bda
a2f4a313bd5b24ab
a2e2ff1f75504cf7
a2f9dc1107c69c48
a2ed595d533e43c3
a2faef811a758bd6
a2f69492ea01038f
a2f41c3e98f313f1
a2f0e00b07e65be4
a2ed89b583ab0b32
a2f86bcdc7c9eb83
a2f8a6b519acb440
a2fd1e6e95e4448a
a2f165ddd67814b7
a2e3523130e06456
a2e7d3f553497c9d
a2e13755c8070c96
a2ea482faa39f467
a2f7cb9d108444ee
a2e589c995a6f4cb
a2e85bbfd86d9401
a2fde01274d2eca5
a2f4c2edf89334a0
a2e1ba5a2c364c8b
a2fa41568fb03478
a2e515a2640fc405
a2e7e2d9d21a0c43
a2e0f3acc8da3c1a
a2e43723e42514d0
a2e0d7fd9ba4ed4d
a2e4664fbf593df8
a2e0027228701562
a2e7126e07f93575
a2e9de5bbb73cd3c
a2e2c7c68528654c
a2e00bfe0b60256b
a2f30bda09e8b524
a2eb00bd3ae44511
a2e487266e86fd0c
a2fb2f78c0ccadbc
a2ec864b10ec3df7
a2e7dcee08e4ddd4
a2f72fbb724f8538
a2fece0d3926ae95
a2f0ea55bf1a1645
a2ea0263eadda653
a2e80f62267a2e8e
a2f2fca72a6a9689
a2f57e2111a976f1
a2ee6ca220cf3693
a2f435af0ba3de97
a2eadbdcb61f3ec1
a2f6c0e3ac48fe3a
a2f3014a228a0667
a2f456492c3dc684
a2f8aa8a22852ff3
a2e9034ae041efac
a2fc35459b259758
a2f1baa15c21a782
a2f4631ef86ff7d1
a2f4df5933549fcc
a2f3dc38be01ef25
a2f94402530b6f4e
a2f8c1870e9597c8
a2e6e629b35d376b
a2eff51434865f92
a2ed2a74101ba7d2
a2ebbdddbdcae702
a2e1e88d9c08e787
a2f107a097368fba
a2eda512bcf7d894
a2e1f0e47c3b4074
a2e1553b93c7b885
a2f7408afa91089c
a2ef469573a40853
a2f7e5ec92e83062
a2fa525ff419f8da
a2ec5c2270bf8074
a2f969dce46d8045
a2ff058fa1c7f849
a2e8dc2b0d7e884e
a2ef79f22ef23019
a2ecac4e581d60bb
a2f8e4a4022998a4
a2fb2d9a2a5bd81d
a2fbe59bbc46a120
a2f94433f99449c5
a2e4297a2c4a8909
a2e415d9352729a8
a2e579775dd439b6
a2f71a3d5e76316e
a2e26083c03c397a
a2f4cb685617a98d
a2e8fcef3a8dd1f9
a2fe5f1c4712d937
a2eca0363d4a39bd
a2f186267d5dc984
a2ff0423b71991e1
a2efe05cf9d1f131
a2ee979e3aa52231
a2e4c672c0e34274
a2efe2faef5cf200
a2f3bc6b0df0ba11
a2e7c24e8760dac8
a2f6bdad16b28262
a2e674493fba929f
a2f2ce2c99bd8a4f
a2efaa90ba8c4287
a2eb910070495a97
a2e7fb0711781a98
a2ee6be18e269a42
a2f26a2c852ee21a
a2f802d191914351
a2fa54fdc2686bb3
a2e8913c8bbebb87
a2e4afaa1150f352
a2eb56f6e0a08b44
a2e03f1db8be7337
a2ed438edc0b6b82
a2ec68aef8e3e369
a2f76b2836063b39
a2f578df074c8bca
a2e83b255b849328
a2ebf2e778d64bdd
a2ec3b482f92e393
a2e6afc0027f3bfc
a2e9cd831a8013f4
a2f9873c40bbbcc9
a2fa4fc1ae568404
a2e61034c6b114a4
a2e2065358ba044b
a2e8c22cca77b4b1
a2fa7189e2746c31
a2fdcebc58381497
a2f33d2fda05eceb
a2f8b8c078cb5cbf
a2ecab059aa2347b
a2fd442eeb969c80
a2e7ce6a12311402
a2fb2404576cdc89
a2eace4903dd7c17
a2fd5f4cd5ea1521
a2fe4911fb419d5c
a2f9d11e5e7fad89
a2e0b0550e5dfd2a
a2fd846ae0aa156d
a2ff72ef4a7005f4
a2f7f2b3d0c48ded
a2f1b5835d3dddca
a2e2970132afe5c1
a2e12b9c27b08d91
a2ee684d66e535a6
a2e58fd0f23c05cb
a2e031c1e4990d94
a2f0869cf30c959b
a2fc5a64bf093679
a2f030984c283fc1
a2f42898c017e6cd
a2f1932dd7c7dec6
a2ff96e30982c7cd
a2ef5af4547ca729
a2ec87368d722722
a2ed0dc40d605fec
a2e3dd2c63775f11
a2eebf0fee6bdfc8
a2e2ff041ddebfde
a2f7b72cbb5d4f44
a2fd9e42dc771f2b
a2ece2265d312fe5
a2fae195ad844736
a2f174b077754749
a2f9542f8c9a5784
a2f1e2bc7f65575f
a2e4e759a180f774
a2fc70604ef1cfe7
a2e35115bb5a886d
a2e2b6ab20e6d8bd
a2e91359c45d088c
a2f2a5f85217e0e0
a2fa6991423a4893
a2ef22e1e2f73011
a2f019d8933b38ec
a2f9bb484145b054
a2e06255e5ccf0e8
a2eb1383233f6048
a2e6d8ace3c5483b
a2f8972dfe69f042
a2eebb9f5938f894
a2f96d8f87222026
a2ffa5c12fce203a
a2f4c27944b218e0
a2ff938ed21b3047
a2f9047fed849919
a2f95ce7dbd6d9e8
a2ece4b2d08f79fe
a2f6b5de30112120
a2ef3324936dc98f
a2e717388ed341e0
a2f834ec000ac1e0
a2f699be3750e9b1
a2fee4c5061101fd
a2facc94153d511f
a2e8f3c15325d992
a2ec154faba169a1
a2fab6a3a4dff984
a2e3536f5690f996
a2fbce3edd31aaa3
a2e0122ce0ff7b9b
a2e7c537d287328a
a2f58fb54541e2cc
a2e908a6c2b13a6f
a2ede01793f24295
a2fea0b788c2caa7
a2ffb055b95242f5
a2f775e2f4416a37
a2e6313b69adfadf
a2e04be7755de21d
a2fc95535c8622e1
a2f46ea056e55a92
a2e9abc1587f6aa9
a2fffc5fd802eb46
a2f9b1215f64eb5d
a2faf28cbe4ab3e6
a2fe2b264e78ab7a
a2e26eb66626b38a
a2f774f93215d3b2
a2e237623ca493f1
a2eb0bbe1020e319
a2fa646b2af40b7d
a2eac3bc22e36bf7
a2fb798535223b15
a2e90c9a4841131c
a2e5ca364fe35b3e
a2e9753a16f74bc7
a2f612bdfe0a23d4
a2ecc754d4eaacbf
a2f5eb4d957094db
a2e134fbfb7edc97
a2f9cfb69e316c24
a2eaa319c2b7345f
a2e5533632101cf5
a2e987e1f0dc84fc
a2e5e7124e0a5448
a2f19fc570147c2a
a2eb17b5b547e49b
a2eb881a6dd814b2
a2e09686c9c40c62
a2e78eaade894dd6
a2ffb38fe47bfd12
a2fa4f2dd18645da
a2e18717b6a89d50
a2ee3562a5f53d99
a2e3f9d35da5f524
a2f35c32cb437521
a2ea91299c794590
a2ed2e26d510058e
a2e776d236bfc501
a2e5e5f1ec6bed7b
a2ffb6c598b215d7
a2e16b794cc9b5e5
a2f29186cb564518
a2e9961dd9902d70
a2f91d28d7382649
a2f9b66914dd86eb
a2f1ce555134a674
a2f662021c24169d
a2fbf4d80a744e77
a2e9c9950d9dbedd
a2e3a505af411e17
a2ecd74fab909e9d
a2edd8044f8ee654
a2e1f48cd7ebe62a
a2fbf3b75e4f2e8b
a2eaf54355e1b6a1
a2e3f09a2d49a635
a2f0730319f7c606
a2ee3ba45f700f82
a2fb711057106f26
a2ee1e5a21945794
a2e5989a1a71ef14
a2e4ffd3add27703
a2f61e002b01efb1
a2ed683b30746ff8
a2e676a6a7e6ff55
a2fa5b1fcbe2c74f
a2f0f876ae8a689c
a2f74cde1518b0f0
a2e38eeb11e388e1
a2f76f98b321d8aa
a2f20d2d69a1580a
a2fb088ad7c9108e
a2e591dfbc8cd072
a2f50c4402a5e82f
a2e370bad3e14893
a2e02ca82aed382d
a2f19934437d10b6
a2eede4c2278581e
a2e87e59b63ee971
a2e19ed90ff4816b
a2efede047d3e183
a2ed2b64d2c589b1
a2fdd8acb1eae12e
a2f59ea35abfe971
a2e3a78353af312f
a2ee35a132b63956
a2f04c5c1f5111b5
a2ef1e30adeae997
a2e6cd371d37e9c7
a2e16f13387ba164
a2fe66ea7dfbe936
a2e8201dda5d1afe
a2e048ceb41daa84
a2ee5cd3bc21ba4d
a2fc2cdd2e852a17
a2e5516e6d87da50
a2f8fe9259c48277
a2e3e0faf4882246
a2f84a382d584a21
a2f8991bfdf2da1f
a2e8d34bd3fd4291
a2e037dfa0a4dae0
a2f3c751bda4b241
a2f7325c0ade229a
a2fd1b1e2bbd825b
a2f5b4084e90e274
a2e3945f970212fc
a2ec82618c199a76
a2e59ce100eea3c9
a2f807a98fb54b12
a2f42b4b974b5b53
a2ec0481f8894bd5
a2e3ad44ca667b7d
a2e49b7b786cf3b5
a2f525d85ebe43b4
a2fff5ee055873e8
a2e9fc0f87a14bc2
a2e67a4e79915b1a
a2f88adbd6728337
a2e188b762e75411
a2fdb8f06761acb9
a2f18e4f1b0954b0
a2f479aaeca8e4ce
a2f6c153af074468
a2e9b53491d1f467
a2e83fe2e24bccdc
a2efffb234feb4e3
a2ee2582f5e3ec6d
a2f3404ee195ac08
a2f1d86c0c1d6ce4
a2ff5d7f552ce447
a2e3f24086c82cd6
a2fe5944422394bc
a2fb1d654bc53475
a2e3e2843eae5c4d
a2e0e32d11bb64ce
a2e25acae5c87510
a2fcdaa5ede615a6
a2fc0fedd0c3bda4
a2e4c42cb23ea593
a2ee65c55801ad2a
a2efca5b7a11bd9e
a2e89ff043ea1576
a2f35be56eb1a566
a2ef4588e1c63d39
a2f292df378a257c
a2f33d4c603d458a
a2f0918470249db8
a2f66dd28662d5c1
a2f440ee4af7adbd
a2e5dd2f0837c6e1
a2f5318bb32166d4
a2f9cf8e8f96f665
a2f505c4a80a76f3
a2f17c5887b70ec1
a2f13734f3f6a651
a2e0e647e6c806c0
a2e4b5522bd7b684
a2f00aec31f21686
a2fff6888604b609
a2ec33c9219c2e06
a2e0cb33ac0b6ea2
a2f20fa9469c96f1
a2e3871b6ecf3f51
a2ed15b7ef5297e3
a2e34b9f541eaf26
a2f66ecf90d65781
a2ef280a7e3b4f3f
a2e59a139b6557d7
a2e4433736ab4749
a2f520ec7e42df69
a2e0e08899be6f48
a2e7b0444f0e673f
a2e9052ac87387a3
a2e37c7a27869fbe
a2eb056597e8c7fc
a2ef57fd79034782
a2fc21a583692fcf
a2f774c59af21800
a2febe8fb4404055
a2efe426b922c02f
a2e83b7fc5691086
a2f4391672e14050
a2e4f5959dad58e5
a2eab8ad446b184b
a2e021421a92c882
a2e3410d3867004a
a2e8bde92620386f
a2f619fe93d9d82e
a2ec9490efcb3119
a2ec715014e25161
a2e5f2968062713c
a2ee1268e80b719a
a2e35129faa181d8
a2eab74cd9775171
a2e272cc9732b10b
a2e455088c1b890e
a2edac772b10b976
a2f99310a52309a2
a2f378eca29fa17d
a2f2114cab96f9f5
a2f6c25232c0a1c5
a2e27f9f8665a1d6
a2f41b90a79039c2
a2e863fa943a2a71
a2fc5dae53fa7abb
a2fdec6f45d9aad7
a2f3858279058262
a2e2d4417109121c
a2ecfc626aeaf203
a2f0bd7bfcf44221
a2f4164c02e7aacd
a2eece3d40284253
a2ed3d4bec643220
a2f64de0314182d3
a2f94ba2e2a7926f
a2f6c3d5e83a42e0
a2f8277a3651526b
a2fe57fc685cb224
a2ed02bd908d7a1e
a2fff6a0788d9352
a2fe035d23754bad
a2f3549797fc23c6
a2efaee73fe34bd4
a2ff8cdf29bae3d1
a2ff51a8d988a301
a2e90f9490fafb5a
a2ea041eed989be5
a2ed6a86b67ef3b4
a2ecac768ce07b86
a2e9226bc128c33d
a2f27a53dcb093b2
a2f08559540bb3f4
a2edd9e67a3ab305
a2fdc1d295ba14b4
a2fdbfc163ca04cb
a2ff88184e3f543a
a2ff67b65385f4a7
a2e3d3c68b53a4b5
a2e7f478e29c5491
a2e152cc78f49c38
a2e247a7961454e6
a2f18aeb05972c4b
a2f4fa41e73524fe
a2f60a03b23934a2
a2eeee72984e8c1b
a2eb9091eda16cf9
a2f6ce56507ecce2
a2eaeddb13a16546
a2f09f88f993d568
a2eaf08c93a475fe
a2eb31b03cdd054b