Skip to content

Commit c8722da

Browse files
committed
[rts] Seek iterators don't need to return facts in lexicographic order
I want to relax the restriction that seek() must create an iterator that returns facts in lexicographic order, because this gives more flexibility in the storage layer. In particular we don't need to sort all the keys, which can be problematic since keys can be large. It turns out that we don't rely on the lexicographic ordering except for saving and resuming query continuations, and this can be done by using the current fact as the checkpoint instead. We can remove the merge() operation on FactIterator and use only append(), and this allows some optimisations when resuming iterators.
1 parent ac55002 commit c8722da

14 files changed

Lines changed: 159 additions & 122 deletions

File tree

glean/rocksdb/database-impl.cpp

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,7 @@ struct SeekIterator final : rts::FactIterator {
306306
} // namespace
307307

308308
std::unique_ptr<rts::FactIterator>
309-
DatabaseImpl::seek(Pid type, folly::ByteRange start, size_t prefix_size) {
310-
assert(prefix_size <= start.size());
309+
DatabaseImpl::seek(Pid type, folly::ByteRange prefix, std::optional<rts::Fact::Ref> restart) {
311310
if (count(type).high() == 0) {
312311
return std::make_unique<EmptyIterator>();
313312
}
@@ -316,22 +315,30 @@ DatabaseImpl::seek(Pid type, folly::ByteRange start, size_t prefix_size) {
316315
binary::Output out;
317316
out.fixed(type);
318317
const auto type_size = out.size();
319-
out.put(start);
320-
return std::make_unique<SeekIterator>(
321-
out.bytes(), type_size + prefix_size, type, this);
318+
319+
if (restart.has_value()) {
320+
auto fact = restart.value();
321+
out.put(fact.clause.key());
322+
return std::make_unique<SeekIterator>(
323+
out.bytes(), type_size + prefix.size(), type, this);
324+
} else {
325+
out.put(prefix);
326+
return std::make_unique<SeekIterator>(
327+
out.bytes(), type_size + prefix.size(), type, this);
328+
}
322329
}
323330

324331
std::unique_ptr<rts::FactIterator> DatabaseImpl::seekWithinSection(
325332
Pid type,
326-
folly::ByteRange start,
327-
size_t prefix_size,
333+
folly::ByteRange prefix,
328334
Id from,
329-
Id upto) {
335+
Id upto,
336+
std::optional<rts::Fact::Ref> restart) {
330337
if (upto <= startingId() || firstFreeId() <= from) {
331338
return std::make_unique<EmptyIterator>();
332339
}
333340

334-
return Section(this, from, upto).seek(type, start, prefix_size);
341+
return Section(this, from, upto).seek(type, prefix, restart);
335342
}
336343

337344
namespace {

glean/rocksdb/database-impl.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,13 @@ struct DatabaseImpl final : Database {
8484
std::unique_ptr<rts::FactIterator> enumerate(Id from, Id upto) override;
8585
std::unique_ptr<rts::FactIterator> enumerateBack(Id from, Id downto) override;
8686
std::unique_ptr<rts::FactIterator>
87-
seek(Pid type, folly::ByteRange start, size_t prefix_size) override;
87+
seek(Pid type, folly::ByteRange prefix, std::optional<rts::Fact::Ref> restart) override;
8888
std::unique_ptr<rts::FactIterator> seekWithinSection(
8989
Pid type,
90-
folly::ByteRange start,
91-
size_t prefix_size,
90+
folly::ByteRange prefix,
9291
Id from,
93-
Id upto) override;
92+
Id upto,
93+
std::optional<rts::Fact::Ref> restart) override;
9494

9595
rts::UsetId getOwner(Id id) override;
9696

glean/rts/benchmarking/factblock.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ bool FactBlock::lookupEachByKey(Lookup& lookup) const {
9191

9292
bool FactBlock::seekToEach(Lookup& lookup) const {
9393
for (const auto fact : *this) {
94-
auto iter = lookup.seek(fact.type, fact.key(), 0);
94+
auto iter = lookup.seek(fact.type, fact.key());
9595
auto ref = iter->get(FactIterator::Demand::KeyOnly);
9696
if (ref.id != fact.id) {
9797
return false;

glean/rts/benchmarking/ffi.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ const char* glean_benchmarking_seek_count(
8383
return ffi::wrap([=] {
8484
size_t n = 0;
8585
for (size_t i = 0; i < pids_count; ++i) {
86-
auto iter = lookup->seek(Pid::fromThrift(pids[i]), {}, 0);
86+
auto iter = lookup->seek(Pid::fromThrift(pids[i]), {});
8787
while (auto ref = iter->get(FactIterator::Demand::KeyOnly)) {
8888
++n;
8989
iter->next();

glean/rts/cache.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -227,18 +227,18 @@ std::unique_ptr<FactIterator> LookupCache::Anchor::enumerateBack(
227227

228228
std::unique_ptr<FactIterator> LookupCache::Anchor::seek(
229229
Pid type,
230-
folly::ByteRange start,
231-
size_t prefix_size) {
232-
return base->seek(type, start, prefix_size);
230+
folly::ByteRange prefix,
231+
std::optional<Fact::Ref> restart) {
232+
return base->seek(type, prefix, restart);
233233
}
234234

235235
std::unique_ptr<FactIterator> LookupCache::Anchor::seekWithinSection(
236236
Pid type,
237-
folly::ByteRange start,
238-
size_t prefix_size,
237+
folly::ByteRange prefix,
239238
Id from,
240-
Id to) {
241-
return base->seekWithinSection(type, start, prefix_size, from, to);
239+
Id to,
240+
std::optional<Fact::Ref> restart) {
241+
return base->seekWithinSection(type, prefix, from, to, restart);
242242
}
243243

244244
void LookupCache::insert(Fact::unique_ptr owned) {

glean/rts/cache.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,14 @@ struct LookupCache {
151151
}
152152

153153
std::unique_ptr<FactIterator>
154-
seek(Pid type, folly::ByteRange start, size_t prefix_size) override;
154+
seek(Pid type, folly::ByteRange prefix, std::optional<Fact::Ref>) override;
155155

156156
std::unique_ptr<FactIterator> seekWithinSection(
157157
Pid type,
158-
folly::ByteRange start,
159-
size_t prefix_size,
158+
folly::ByteRange prefix,
160159
Id from,
161-
Id to) override;
160+
Id to,
161+
std::optional<Fact::Ref>) override;
162162

163163
UsetId getOwner(Id id) override {
164164
return base->getOwner(id);

glean/rts/factset.cpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
#include "glean/rts/factset.h"
10+
#include "glean/rts/binary.h"
1011

1112
namespace facebook {
1213
namespace glean {
@@ -242,14 +243,28 @@ FactSet::seek(Pid type, folly::ByteRange start, size_t prefix_size) {
242243
}
243244
}
244245

246+
std::unique_ptr<FactIterator>
247+
FactSet::seek(Pid type, folly::ByteRange prefix, std::optional<Fact::Ref> restart) {
248+
if (restart.has_value()) {
249+
auto id = restart.value().id;
250+
if (id < facts.startingId() || id >= facts.startingId() + facts.size()) {
251+
error("seek: restart Id not within set");
252+
}
253+
auto key = facts[id - facts.startingId()].clause.key();
254+
return seek(type, key, prefix.size());
255+
} else {
256+
return seek(type, prefix, prefix.size());
257+
}
258+
}
259+
245260
std::unique_ptr<FactIterator> FactSet::seekWithinSection(
246261
Pid type,
247-
folly::ByteRange start,
248-
size_t prefix_size,
262+
folly::ByteRange prefix,
249263
Id from,
250-
Id to) {
264+
Id to,
265+
std::optional<Fact::Ref> restart) {
251266
if (from <= startingId() && firstFreeId() <= to) {
252-
return seek(type, start, prefix_size);
267+
return seek(type, prefix, restart);
253268
}
254269

255270
// We have no use case for actually performing a bounded

glean/rts/factset.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -284,14 +284,14 @@ struct FactSet final : public Define {
284284
/// slow. The first call for each predicate will be especially slow as it will
285285
/// need to create an index.
286286
std::unique_ptr<FactIterator>
287-
seek(Pid type, folly::ByteRange start, size_t prefix_size) override;
287+
seek(Pid type, folly::ByteRange prefix, std::optional<Fact::Ref>) override;
288288

289289
std::unique_ptr<FactIterator> seekWithinSection(
290290
Pid type,
291-
folly::ByteRange start,
292-
size_t prefix_size,
291+
folly::ByteRange prefix,
293292
Id from,
294-
Id to) override;
293+
Id to,
294+
std::optional<Fact::Ref>) override;
295295

296296
UsetId getOwner(Id) override {
297297
return INVALID_USET;
@@ -350,6 +350,9 @@ struct FactSet final : public Define {
350350
/// don't do seeks on FactSets.
351351
struct Index;
352352
OnDemand<Index> index;
353+
354+
std::unique_ptr<FactIterator>
355+
seek(Pid type, folly::ByteRange start, size_t prefix_size);
353356
};
354357

355358
} // namespace rts

glean/rts/lookup.cpp

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -100,20 +100,6 @@ struct MergeIterator final : public FactIterator {
100100

101101
} // namespace
102102

103-
std::unique_ptr<FactIterator> FactIterator::merge(
104-
std::unique_ptr<FactIterator> left,
105-
std::unique_ptr<FactIterator> right,
106-
size_t prefix_size) {
107-
if (left->get(Demand::KeyOnly)) {
108-
return right->get(Demand::KeyOnly)
109-
? std::make_unique<MergeIterator>(
110-
std::move(left), std::move(right), prefix_size)
111-
: std::move(left);
112-
} else {
113-
return right;
114-
}
115-
}
116-
117103
std::unique_ptr<FactIterator> Section::enumerate(Id from, Id upto) {
118104
if (upto <= lowBoundary() || highBoundary() <= from) {
119105
return std::make_unique<EmptyIterator>();
@@ -135,7 +121,7 @@ std::unique_ptr<FactIterator> Section::enumerateBack(Id from, Id downto) {
135121
}
136122

137123
std::unique_ptr<FactIterator>
138-
Section::seek(Pid type, folly::ByteRange start, size_t prefix_size) {
124+
Section::seek(Pid type, folly::ByteRange prefix, std::optional<Fact::Ref> restart) {
139125
struct Iterator final : FactIterator {
140126
Iterator(std::unique_ptr<FactIterator> base, Id upto, Id from)
141127
: base_(std::move(base)), high_boundary_(upto), low_boundary_(from) {}
@@ -170,19 +156,19 @@ Section::seek(Pid type, folly::ByteRange start, size_t prefix_size) {
170156
Id low_boundary_;
171157
};
172158
return std::make_unique<Iterator>(
173-
base()->seek(type, start, prefix_size), highBoundary(), lowBoundary());
159+
base()->seek(type, prefix, restart), highBoundary(), lowBoundary());
174160
}
175161

176162
std::unique_ptr<FactIterator> Section::seekWithinSection(
177163
Pid type,
178-
folly::ByteRange start,
179-
size_t prefix_size,
164+
folly::ByteRange prefix,
180165
Id from,
181-
Id upto) {
166+
Id upto,
167+
std::optional<Fact::Ref> restart) {
182168
if (from <= lowBoundary() && highBoundary() <= upto) {
183-
return seek(type, start, prefix_size);
169+
return seek(type, prefix, restart);
184170
} else {
185-
return Section(base_, from, upto).seek(type, start, prefix_size);
171+
return Section(base_, from, upto).seek(type, prefix, restart);
186172
}
187173
}
188174

glean/rts/lookup.h

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@ struct FactIterator {
4040
virtual std::optional<Id> lower_bound() = 0;
4141
virtual std::optional<Id> upper_bound() = 0;
4242

43-
static std::unique_ptr<FactIterator> merge(
44-
std::unique_ptr<FactIterator> left,
45-
std::unique_ptr<FactIterator> right,
46-
size_t prefix_size);
47-
4843
static std::unique_ptr<FactIterator> append(
4944
std::unique_ptr<FactIterator> left,
5045
std::unique_ptr<FactIterator> right);
@@ -127,23 +122,27 @@ struct Lookup {
127122
Id from = Id::invalid(),
128123
Id downto = Id::invalid()) = 0;
129124

130-
// Obtain a prefix iterator for the given predicate. The iterator covers keys
131-
// which begin with the first 'prefix_size' bytes of 'start', starting with
132-
// the first key not lexicographically less than 'start', and produces facts
133-
// in lexicographic order. In particular, setting 'prefix_size' to
134-
// 'start.size()' iterates over all keys with the prefix 'start'.
125+
// Obtain a prefix iterator for the given predicate. The iterator will return
126+
// each key with the given prefix exactly once, in no specified order.
127+
//
128+
// If `restart` is non-empty, the fact must be within the iterator's
129+
// range; the iterator will be positioned at the given fact. This
130+
// can be used to resume a previous iterator.
135131
virtual std::unique_ptr<FactIterator>
136-
seek(Pid type, folly::ByteRange start, size_t prefix_size) = 0;
132+
seek(Pid type,
133+
folly::ByteRange prefix,
134+
std::optional<Fact::Ref> restart = {}) = 0;
137135

138136
// Perform a seek on a section of the Lookup.
139137
// Results will have Ids within the range [from, utpto).
140138
// Facts can reference Ids outside of the specified range.
139+
// See `seek()` for the meaning of `restart`.
141140
virtual std::unique_ptr<FactIterator> seekWithinSection(
142141
Pid type,
143-
folly::ByteRange start,
144-
size_t prefix_size,
142+
folly::ByteRange prefix,
145143
Id from,
146-
Id to) = 0;
144+
Id to,
145+
std::optional<Fact::Ref> restart = {}) = 0;
147146

148147
virtual UsetId getOwner(Id id) = 0;
149148
};
@@ -183,12 +182,20 @@ struct EmptyLookup final : Lookup {
183182
return std::make_unique<EmptyIterator>();
184183
}
185184

186-
std::unique_ptr<FactIterator> seek(Pid, folly::ByteRange, size_t) override {
185+
std::unique_ptr<FactIterator> seek(
186+
Pid,
187+
folly::ByteRange,
188+
std::optional<Fact::Ref>) override {
187189
return std::make_unique<EmptyIterator>();
188190
}
189191

190192
std::unique_ptr<FactIterator>
191-
seekWithinSection(Pid, folly::ByteRange, size_t, Id, Id) override {
193+
seekWithinSection(
194+
Pid,
195+
folly::ByteRange,
196+
Id,
197+
Id,
198+
std::optional<Fact::Ref>) override {
192199
return std::make_unique<EmptyIterator>();
193200
}
194201

@@ -239,14 +246,14 @@ struct Section : Lookup {
239246
std::unique_ptr<FactIterator> enumerateBack(Id from, Id downto) override;
240247

241248
std::unique_ptr<FactIterator>
242-
seek(Pid type, folly::ByteRange start, size_t prefix_size) override;
249+
seek(Pid type, folly::ByteRange prefix, std::optional<Fact::Ref>) override;
243250

244251
std::unique_ptr<FactIterator> seekWithinSection(
245252
Pid type,
246-
folly::ByteRange start,
247-
size_t prefix_size,
253+
folly::ByteRange prefix,
248254
Id from,
249-
Id to) override;
255+
Id to,
256+
std::optional<Fact::Ref>) override;
250257

251258
UsetId getOwner(Id id) override {
252259
return isWithinBounds(id) ? base()->getOwner(id) : INVALID_USET;

0 commit comments

Comments
 (0)