Skip to content

Commit 828ef53

Browse files
committed
name polishing for consistency
1 parent 756f24e commit 828ef53

File tree

3 files changed

+49
-48
lines changed

3 files changed

+49
-48
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,15 @@ Some parameters are controlled from the file URI's query string opening the data
117117
| | URI query parameters | PRAGMA |
118118
| -- | -- | -- |
119119
| writing/compression | <ul><li>level</li><li>threads</li><li>outer_page_size</li><li>outer_unsafe</li></ul> | <ul><li>page_size</li><li>auto_vacuum</li><li>journal_mode</li></ul> |
120-
| reading/decompression | <ul><li>outer_cache_size</li></ul> | <ul><li>cache_size</li></ul> |
120+
| reading/decompression | <ul><li>outer_cache_size</li><li>noprefetch</li></ul> | <ul><li>cache_size</li></ul> |
121121

122122
* **&level=3**: Zstandard compression level for newly written pages (-7 to 22)
123-
* **&threads=1**: background compression threads (-1 to match host processors)
123+
* **&threads=1**: thread budget for page compression & prefetching (-1 to match host processors)
124124
* **&outer_page_size=4096**: page size for the newly-created outer database; suggest doubling the (inner) page_size, to reduce space overhead from packing the compressed inner pages
125125
* **&outer_unsafe=false**: set true to speed up bulk load by disabling transaction safety for outer database (app crash easily causes corruption)
126126

127127
* **&outer_cache_size=-2000**: page cache size for outer database, in [PRAGMA cache_size](https://www.sqlite.org/pragma.html#pragma_cache_size) units. Limited effect if on SSD.
128+
* **&noprefetch=false**: set true to disable background prefetching/decompression even if threads>1. Prefetching is counterproductive if page size is too small or CPU cycles are scarce.
128129

129130
* **PRAGMA page_size=4096**: uncompressed [page size](https://www.sqlite.org/pragma.html#pragma_page_size) for the newly-created inner database. Larger pages are more compressible, but increase [read/write amplification](http://smalldatum.blogspot.com/2015/11/read-write-space-amplification-pick-2_23.html). YMMV but 8 or 16 KiB have been working well for us.
130131
* **PRAGMA auto_vacuum=NONE**: set to FULL or INCREMENTAL on a newly-created database if you expect its size to fluctuate over time, so that the file will [shrink to fit](https://www.sqlite.org/pragma.html#pragma_auto_vacuum). (The outer database auto-vacuums when it's closed.)

src/SQLiteNestedVFS.h

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* is useless, but subclasses can easily layer in compression and/or encryption codecs.
99
*
1010
* Uses thread pools so that (i) page encoding and writeback occur on background threads, and (ii)
11-
* page reads and decoding can be "prefetched" in the background during detected sequential scans.
11+
* pages can be prefetched and decoded on background threads during detected sequential scans.
1212
* These schemes are each a bit complex but at least they're separate: when asked to read a page,
1313
* we first wait for any outstanding writes to finish, and vice-versa.
1414
*/
@@ -120,7 +120,7 @@ class InnerDatabaseFile : public SQLiteVFS::File {
120120

121121
int FileSize(sqlite3_int64 *pSize) override {
122122
try {
123-
FinishUpserts();
123+
UpsertBarrier();
124124
*pSize = DetectPageSize() * DetectPageCount();
125125
return SQLITE_OK;
126126
} catch (std::exception &exn) {
@@ -144,7 +144,7 @@ class InnerDatabaseFile : public SQLiteVFS::File {
144144
// 5. Little thread synchronization overhead is affordable, as Zstandard decompression of a
145145
// SQLite page (4-64 KiB) takes only 1-10 microseconds -- about 10x time to memcpy.
146146
// Subclasses may in turn subclass this to add their own logic to SeekCursor and DecodePage.
147-
struct PageFetchJob {
147+
struct FetchJob {
148148
enum State { NEW, QUEUE, WIP, DONE };
149149
// NEW = idle, QUEUE = ready & waiting, WIP = work in progress, DONE = complete or error
150150
// QUEUE=>WIP is the only contentious transition; background threads and the main thread
@@ -176,15 +176,15 @@ class InnerDatabaseFile : public SQLiteVFS::File {
176176
std::chrono::nanoseconds t_seek = std::chrono::nanoseconds::zero(),
177177
t_decode = std::chrono::nanoseconds::zero();
178178

179-
PageFetchJob(InnerDatabaseFile &that)
179+
FetchJob(InnerDatabaseFile &that)
180180
: cursor(*(that.outer_db_), "SELECT pageno, data, meta1, meta2 FROM " +
181181
that.inner_db_pages_table_ +
182182
" WHERE pageno >= ? ORDER BY pageno"),
183183
page_size(that.page_size_) {
184184
PutState(State::NEW);
185185
}
186186

187-
virtual ~PageFetchJob() {}
187+
virtual ~FetchJob() {}
188188

189189
inline void *EffectiveDest() noexcept {
190190
return dest ? dest : (decodebuf.resize(page_size), decodebuf.data());
@@ -286,12 +286,12 @@ class InnerDatabaseFile : public SQLiteVFS::File {
286286
};
287287

288288
// Override me!
289-
virtual std::unique_ptr<PageFetchJob> NewPageFetchJob() {
290-
return std::unique_ptr<PageFetchJob>(new PageFetchJob(*this));
289+
virtual std::unique_ptr<FetchJob> NewFetchJob() {
290+
return std::unique_ptr<FetchJob>(new FetchJob(*this));
291291
}
292292

293293
const size_t MAX_FETCH_CURSORS = 4;
294-
std::vector<std::unique_ptr<PageFetchJob>> fetch_jobs_;
294+
std::vector<std::unique_ptr<FetchJob>> fetch_jobs_;
295295
ThreadPool fetch_thread_pool_;
296296
std::mutex seek_lock_; // serializes outer db interactions among fetch background threads
297297
std::atomic<bool> seek_interrupt_; // broadcast that main thread wants seek_lock_
@@ -300,15 +300,15 @@ class InnerDatabaseFile : public SQLiteVFS::File {
300300
sqlite3_int64 longest_read_ = 0;
301301

302302
void *BackgroundFetchJob(void *ctx) noexcept {
303-
PageFetchJob *job = (PageFetchJob *)ctx;
303+
FetchJob *job = (FetchJob *)ctx;
304304
std::unique_lock<std::mutex> seek_lock(seek_lock_);
305305
while (seek_interrupt_.load(std::memory_order_relaxed)) {
306306
// yield to main thread
307307
seek_lock.unlock();
308308
std::this_thread::yield();
309309
seek_lock.lock();
310310
}
311-
if (!job->TransitionState(PageFetchJob::State::QUEUE, PageFetchJob::State::WIP)) {
311+
if (!job->TransitionState(FetchJob::State::QUEUE, FetchJob::State::WIP)) {
312312
return nullptr; // they took our job!!!!
313313
}
314314
job->Execute(&seek_lock);
@@ -324,16 +324,15 @@ class InnerDatabaseFile : public SQLiteVFS::File {
324324
bool can_prefetch = fetch_thread_pool_.MaxThreads() > 1;
325325

326326
// Is there already a background job to prefetch the desired page?
327-
PageFetchJob *job = nullptr;
327+
FetchJob *job = nullptr;
328328
bool foreground = false;
329329
if (can_prefetch) {
330330
for (auto job_i = fetch_jobs_.begin(); job_i != fetch_jobs_.end(); job_i++) {
331331
if ((*job_i)->pageno == pageno) {
332-
assert((*job_i)->GetState() > PageFetchJob::State::NEW);
332+
assert((*job_i)->GetState() > FetchJob::State::NEW);
333333
job = job_i->get();
334334
// If yes & it hasn't yet started, race to run it here in the foreground
335-
foreground =
336-
job->TransitionState(PageFetchJob::State::QUEUE, PageFetchJob::State::WIP);
335+
foreground = job->TransitionState(FetchJob::State::QUEUE, FetchJob::State::WIP);
337336
break;
338337
}
339338
}
@@ -343,7 +342,7 @@ class InnerDatabaseFile : public SQLiteVFS::File {
343342
if (!job) {
344343
for (auto job_i = fetch_jobs_.begin(); job_i != fetch_jobs_.end(); job_i++) {
345344
auto st = (*job_i)->GetState();
346-
if (st == PageFetchJob::State::NEW || st == PageFetchJob::State::DONE) {
345+
if (st == FetchJob::State::NEW || st == FetchJob::State::DONE) {
347346
if ((*job_i)->cursor_pageno + 1 == pageno) {
348347
job = job_i->get();
349348
break;
@@ -357,17 +356,17 @@ class InnerDatabaseFile : public SQLiteVFS::File {
357356
if (!job || (fetch_jobs_.size() < MAX_FETCH_CURSORS &&
358357
job->cursor_pageno + 1 != pageno && job->cursor_pageno)) {
359358
assert(fetch_jobs_.size() < MAX_FETCH_CURSORS);
360-
fetch_jobs_.push_back(NewPageFetchJob());
359+
fetch_jobs_.push_back(NewFetchJob());
361360
job = fetch_jobs_.back().get();
362361
}
363362
#ifndef NDEBUG
364-
if (job->GetState() == PageFetchJob::State::DONE && job->cursor_pageno) {
363+
if (job->GetState() == FetchJob::State::DONE && job->cursor_pageno) {
365364
++prefetch_wasted_;
366365
}
367366
#endif
368367
job->Renew();
369368
job->pageno = pageno;
370-
job->PutState(PageFetchJob::State::WIP);
369+
job->PutState(FetchJob::State::WIP);
371370
foreground = true;
372371
}
373372

@@ -386,10 +385,10 @@ class InnerDatabaseFile : public SQLiteVFS::File {
386385
} else {
387386
job->Execute();
388387
}
389-
assert(job->GetState() == PageFetchJob::State::DONE);
388+
assert(job->GetState() == FetchJob::State::DONE);
390389
} else {
391390
// Semi-busy-wait for background job to finish
392-
while (job->GetState() < PageFetchJob::State::DONE) {
391+
while (job->GetState() < FetchJob::State::DONE) {
393392
std::this_thread::yield();
394393
}
395394
#ifndef NDEBUG
@@ -400,6 +399,7 @@ class InnerDatabaseFile : public SQLiteVFS::File {
400399
if (!job->errmsg.empty()) {
401400
_DBG << job->errmsg << _EOL;
402401
job->Renew();
402+
PrefetchBarrier();
403403
throw SQLite::Exception(job->errmsg, SQLITE_IOERR_READ);
404404
}
405405

@@ -422,15 +422,15 @@ class InnerDatabaseFile : public SQLiteVFS::File {
422422
for (auto job_i = fetch_jobs_.begin(); job_i != fetch_jobs_.end(); job_i++) {
423423
if (job_i->get() != job) {
424424
auto st = (*job_i)->GetState();
425-
if (st == PageFetchJob::State::QUEUE || st == PageFetchJob::State::WIP) {
425+
if (st == FetchJob::State::QUEUE || st == FetchJob::State::WIP) {
426426
++active_jobs;
427427
}
428428
}
429429
}
430430
// always leave at least one slot free for a non-sequential read to use
431431
if (active_jobs + 2 <= fetch_thread_pool_.MaxThreads()) {
432432
job->pageno = pageno_hint;
433-
job->PutState(PageFetchJob::State::QUEUE);
433+
job->PutState(FetchJob::State::QUEUE);
434434
fetch_thread_pool_.Enqueue(
435435
job, [this](void *job) { return this->BackgroundFetchJob(job); }, nullptr);
436436
} else {
@@ -467,7 +467,7 @@ class InnerDatabaseFile : public SQLiteVFS::File {
467467
if (iAmt == 0) {
468468
return SQLITE_OK;
469469
}
470-
FinishUpserts();
470+
UpsertBarrier();
471471
if (!DetectPageSize()) { // file is empty
472472
memset(zBuf, 0, iAmt);
473473
return iAmt > 0 ? SQLITE_IOERR_SHORT_READ : SQLITE_OK;
@@ -532,7 +532,7 @@ class InnerDatabaseFile : public SQLiteVFS::File {
532532
seek_interrupt_.store(false, std::memory_order_relaxed);
533533
}
534534
for (auto &job : fetch_jobs_) {
535-
if (job->TransitionState(PageFetchJob::State::QUEUE, PageFetchJob::State::NEW)) {
535+
if (job->TransitionState(FetchJob::State::QUEUE, FetchJob::State::NEW)) {
536536
job->Renew();
537537
}
538538
}
@@ -542,8 +542,8 @@ class InnerDatabaseFile : public SQLiteVFS::File {
542542
}
543543
// wipe all prefetch cursors
544544
for (auto &job : fetch_jobs_) {
545-
assert(job->GetState() == PageFetchJob::State::NEW ||
546-
job->GetState() == PageFetchJob::State::DONE);
545+
assert(job->GetState() == FetchJob::State::NEW ||
546+
job->GetState() == FetchJob::State::DONE);
547547
job->Renew();
548548
job->ResetCursor();
549549
}
@@ -614,7 +614,7 @@ class InnerDatabaseFile : public SQLiteVFS::File {
614614
std::vector<std::unique_ptr<EncodeJob>> encode_job_pool_;
615615
std::mutex encode_job_pool_mutex_;
616616

617-
ThreadPool thread_pool_;
617+
ThreadPool upsert_thread_pool_;
618618
std::string upsert_errmsg_;
619619

620620
// enqueue page encoding+upsert on thread pool
@@ -637,7 +637,7 @@ class InnerDatabaseFile : public SQLiteVFS::File {
637637
assert(job->insert ? pageno == page_count_ + 1 : pageno <= page_count_);
638638
page_count_ += job->insert ? 1 : 0;
639639
// use ThreadPool to run encoding jobs in parallel and upsert jobs serially
640-
thread_pool_.Enqueue(
640+
upsert_thread_pool_.Enqueue(
641641
job.release(),
642642
[](void *job) noexcept {
643643
((EncodeJob *)job)->Execute();
@@ -651,8 +651,8 @@ class InnerDatabaseFile : public SQLiteVFS::File {
651651
throw;
652652
}
653653

654-
if (thread_pool_.MaxThreads() == 1) {
655-
FinishUpserts();
654+
if (upsert_thread_pool_.MaxThreads() == 1) {
655+
UpsertBarrier();
656656
}
657657
}
658658

@@ -695,8 +695,8 @@ class InnerDatabaseFile : public SQLiteVFS::File {
695695
}
696696

697697
// wait for background upserts to complete + raise any error message
698-
void FinishUpserts(bool ignore_error = false) {
699-
thread_pool_.Barrier();
698+
void UpsertBarrier(bool ignore_error = false) {
699+
upsert_thread_pool_.Barrier();
700700
if (!ignore_error && !upsert_errmsg_.empty()) {
701701
throw SQLite::Exception(upsert_errmsg_, SQLITE_IOERR_WRITE);
702702
}
@@ -747,7 +747,7 @@ class InnerDatabaseFile : public SQLiteVFS::File {
747747
return SQLITE_OK;
748748
} catch (std::exception &exn) {
749749
_DBG << exn.what() << _EOL;
750-
FinishUpserts(true);
750+
UpsertBarrier(true);
751751
page_count_ = 0; // to be redetected
752752
return SQLITE_IOERR_WRITE;
753753
}
@@ -757,7 +757,7 @@ class InnerDatabaseFile : public SQLiteVFS::File {
757757
assert(!read_only_);
758758
try {
759759
PrefetchBarrier();
760-
FinishUpserts();
760+
UpsertBarrier();
761761
if (!DetectPageSize()) {
762762
return size == 0 ? SQLITE_OK : SQLITE_IOERR_TRUNCATE;
763763
}
@@ -790,7 +790,7 @@ class InnerDatabaseFile : public SQLiteVFS::File {
790790
int Sync(int flags) override {
791791
assert(!read_only_);
792792
try {
793-
FinishUpserts();
793+
UpsertBarrier();
794794
} catch (std::exception &exn) {
795795
_DBG << exn.what() << _EOL;
796796
page_count_ = 0; // to be redetected
@@ -861,7 +861,7 @@ class InnerDatabaseFile : public SQLiteVFS::File {
861861
PrefetchBarrier();
862862
if (!read_only_) {
863863
int rc;
864-
if ((rc = Sync(0)) != SQLITE_OK) { // includes FinishUpserts
864+
if ((rc = Sync(0)) != SQLITE_OK) { // includes UpsertBarrier
865865
return rc;
866866
}
867867
outer_db_->exec("PRAGMA incremental_vacuum");
@@ -925,7 +925,7 @@ class InnerDatabaseFile : public SQLiteVFS::File {
925925
// MAX(pageno) instead of COUNT(pageno) because the latter would trigger table scan
926926
select_page_count_(*outer_db_,
927927
"SELECT IFNULL(MAX(pageno), 0) FROM " + inner_db_pages_table_),
928-
thread_pool_(threads, threads * 3),
928+
upsert_thread_pool_(threads, threads * 3),
929929
fetch_thread_pool_(std::min(noprefetch ? 1 : threads, MAX_FETCH_CURSORS),
930930
MAX_FETCH_CURSORS),
931931
seek_interrupt_(false) {

src/zstd_vfs.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,15 @@ class ZstdInnerDatabaseFile : public SQLiteNested::InnerDatabaseFile {
110110
return dict_cache_[dict_id];
111111
}
112112

113-
struct ZstdPageFetchJob : public super::PageFetchJob {
113+
struct ZstdFetchJob : public super::FetchJob {
114114
ZSTD_DCtx *dctx = nullptr;
115115
const ZSTD_DDict *ddict = nullptr;
116116
bool plain = false; // uncompressed page
117117
ZstdInnerDatabaseFile *that;
118118

119-
ZstdPageFetchJob(ZstdInnerDatabaseFile &that_) : super::PageFetchJob(that_), that(&that_) {}
119+
ZstdFetchJob(ZstdInnerDatabaseFile &that_) : super::FetchJob(that_), that(&that_) {}
120120

121-
~ZstdPageFetchJob() {
121+
~ZstdFetchJob() {
122122
if (dctx) {
123123
ZSTD_freeDCtx(dctx);
124124
}
@@ -128,7 +128,7 @@ class ZstdInnerDatabaseFile : public SQLiteNested::InnerDatabaseFile {
128128
// dictionary ready for use. This has to be done in this serialized method since it may
129129
// need to load it from the outer db.
130130
void SeekCursor() override {
131-
super::PageFetchJob::SeekCursor();
131+
super::FetchJob::SeekCursor();
132132
#ifndef NDEBUG
133133
auto t0 = std::chrono::high_resolution_clock::now();
134134
#endif
@@ -157,7 +157,7 @@ class ZstdInnerDatabaseFile : public SQLiteNested::InnerDatabaseFile {
157157
auto t0 = std::chrono::high_resolution_clock::now();
158158
#endif
159159
if (plain) { // uncompressed page
160-
return super::PageFetchJob::DecodePage();
160+
return super::FetchJob::DecodePage();
161161
}
162162
if (!dctx) {
163163
dctx = ZSTD_createDCtx();
@@ -181,8 +181,8 @@ class ZstdInnerDatabaseFile : public SQLiteNested::InnerDatabaseFile {
181181
}
182182
};
183183

184-
std::unique_ptr<super::PageFetchJob> NewPageFetchJob() override {
185-
return std::unique_ptr<super::PageFetchJob>(new ZstdPageFetchJob(*this));
184+
std::unique_ptr<super::FetchJob> NewFetchJob() override {
185+
return std::unique_ptr<super::FetchJob>(new ZstdFetchJob(*this));
186186
}
187187

188188
// dict currently used for compression of new pages
@@ -214,7 +214,7 @@ class ZstdInnerDatabaseFile : public SQLiteNested::InnerDatabaseFile {
214214

215215
if (cur_dict_ < 0) {
216216
PrefetchBarrier();
217-
FinishUpserts();
217+
UpsertBarrier();
218218
// start off with the newest dict stored in the database, if any
219219
if (!last_dict_id_) {
220220
last_dict_id_.reset(
@@ -235,7 +235,7 @@ class ZstdInnerDatabaseFile : public SQLiteNested::InnerDatabaseFile {
235235
std::max(page_count_, cur_dict_pages_written_) >= 3 * cur_dict_page_count_) {
236236
// time to create a new dict...
237237
PrefetchBarrier();
238-
FinishUpserts();
238+
UpsertBarrier();
239239
auto t0 = std::chrono::high_resolution_clock::now();
240240

241241
// read random pages

0 commit comments

Comments
 (0)