Skip to content

Add CacheSQLite#13131

Merged
Frenzie merged 7 commits into
koreader:masterfrom
Frenzie:cachesqlite
Jan 29, 2025
Merged

Add CacheSQLite#13131
Frenzie merged 7 commits into
koreader:masterfrom
Frenzie:cachesqlite

Conversation

@Frenzie

@Frenzie Frenzie commented Jan 24, 2025

Copy link
Copy Markdown
Member

Persist is intended for larger objects, which works best for things like cached tiles with some metadata.

For many small to medium entries that you nevertheless don't necessarily want to keep around in memory, that seems a bit inefficient.

SQLite then, is effectively a way of minimizing the amount of writes in this scenario.

Relates to #13061. Depends on #13122.


This change is Reviewable

Persist is intended for larger objects, which works best for things like cached tiles with some metadata.

For many small to medium entries that you nevertheless don't necessarily want to keep around in memory, that seems a bit inefficient.

SQLite then, is effectively a way of minimizing the amount of writes in this scenario.

Relates to koreader#13061. Depends on koreader#13122.
@NiLuJe

NiLuJe commented Jan 25, 2025

Copy link
Copy Markdown
Member

Sounds good!

Random query: would an in-memory db variant make sense in some of those use-cases?

Comment thread frontend/cachesqlite.lua Outdated
UPDATE cache SET last_access = ? WHERE key = ?
RETURNING value;
]])
local row = stmt:reset():bind(os.time(), key):step()

@NiLuJe NiLuJe Jan 25, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Comment valid across all the os.time calls)

time.now instead?

Or even UIManager:getTime() if a coarser, cached value from our last UI frame is sufficient?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that os.time() is human-legible when looking at the table, fwiw. UIManager:getTime() probably makes the most sense as an alternative, or in any case I don't see what higher granularity could be desired for.

Screenshot_20250125_222535

@Frenzie

Frenzie commented Jan 25, 2025

Copy link
Copy Markdown
Member Author

Random query: would an in-memory db variant make sense in some of those use-cases?

I toyed with something like self.db = SQ3.open(self.db_path or ":memory:") initially but I removed it because I didn't really see the difference with just using Lua (aka regular Cache). It shouldn't be much effort if you want it though. Besides :memory: the open/close calls should be changed a bit to act differently in that case.

@Frenzie

Frenzie commented Jan 25, 2025

Copy link
Copy Markdown
Member Author

Come to think of it, it'd probably be better to add an auto_close toggle, or something alone those lines, since fully controlling it yourself could also be useful with a file. Then you can simply set db_path to :memory: and it's not a special scenario.

Comment thread frontend/cachesqlite.lua
self.db:exec[[
CREATE TABLE IF NOT EXISTS cache (
key TEXT PRIMARY KEY,
value BLOB,

@Frenzie Frenzie Jan 25, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I departed from Cache because it mostly met my needs. It's just that the writing of the cache to disk would've resulted in many excess writes. But while writing it I therefore didn't consider that this part could be more dynamic to take better advantage of SQLite. I don't think it matters in the near future though.

Edit: by which I mean maybe for some use case you don't need any blobs, maybe you just need a few text columns instead.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://www.sqlite.org/datatype3.html

SQLite uses a more general dynamic type system. In SQLite, the datatype of a value is associated with the value itself, not with its container.

So, it's my understanding the column type you set in a CREATE TABLE is just documentation, and may be value CRAP works :)

@Frenzie Frenzie Jan 26, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not referring to the types, or at least not exactly, just the number of columns and their names. This type is effectively "there's only 1 column so you can't sanely use SQL on it".

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I then still don't understand what you are referring to :)
What would be the alternative you end up not using/needing?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't think of it until just last night when I wrote that, but the cache in question could define its own custom columns. My entire thinking was based around passing in serialized Lua objects.

If your object is merely {title="this", author="that"}, maybe you'd be better served with two regular columns. I'm not entirely sure how useful such a thing would be, but there are some potential secondary use cases based on what's in the cache that'd be more efficient written in SQL than by doing a lot of Lua looping asking for rows one at a time.

In that case the cache insertion and associated auto-deletion logic would still be the reason to use the cache, but for certain more advanced interactions you could do something like cache:getDBConn():prepare(etc) to run custom queries on the data.

@Frenzie Frenzie added this to the 2025.01 milestone Jan 29, 2025
@Frenzie Frenzie merged commit 63bf9d5 into koreader:master Jan 29, 2025
@Frenzie Frenzie deleted the cachesqlite branch January 29, 2025 20:33
0xstillb pushed a commit to 0xstillb/koreader-thai that referenced this pull request May 9, 2026
Persist is intended for larger objects, which works best for things like cached tiles with some metadata.

For many small to medium entries that you nevertheless don't necessarily want to keep around in memory, that seems a bit inefficient.

SQLite then, is effectively a way of minimizing the amount of writes in this scenario.

Relates to koreader#13061. Depends on koreader#13122.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants