Skip to content

Import sqlite storage as separate module#3449

Open
badboy wants to merge 3 commits intomain-sqlitefrom
sqlite/initial-import
Open

Import sqlite storage as separate module#3449
badboy wants to merge 3 commits intomain-sqlitefrom
sqlite/initial-import

Conversation

@badboy
Copy link
Copy Markdown
Member

@badboy badboy commented May 6, 2026

My plan over the next few weeks is to split up #3405 into reviewable pieces and merge them piece by piece.
For that I created the main-sqlite branch, which currently is the same as main. This way any feature work/bug fixes against the current Glean can continue to go to main without issues.
Over time I can rebase/merge that branch against main to keep-up-to-date, but keep further sqlite work reviewable.


this very first part is merely adding the first pieces of code, but none of that is actually part of the compilation yet (wouldn't work because it's not fully usable yet).
The details about the sqlite setup will change in later commits. Re-arranging that into a single commit that has the perfect setup would be a lot of work of splitting and rebasing commits (and squashing and merge conflicts), so doing it this way seemed easier.

badboy added 2 commits May 6, 2026 14:57
This is a modified version of the kvstore/skv implementation:
https://searchfox.org/firefox-main/rev/cced10961b53e0d29e22e635404fec37728b2644/toolkit/components/kvstore/src/skv/connection.rs
Which itself is based on application-service's sql-support.

It's stripped down to what we need in Glean:
* A file-backed database
* A schema set up on start, potentially applying migrations if we need that
* A read-write connection, which is re-used for all access.
@badboy badboy requested a review from a team as a code owner May 6, 2026 13:02
@badboy badboy requested review from chutten and removed request for a team May 6, 2026 13:02
@badboy badboy force-pushed the sqlite/initial-import branch from b65fc42 to d3d4e1b Compare May 6, 2026 13:04
Comment on lines +25 to +28
"PRAGMA journal_mode = WAL;
PRAGMA journal_size_limit = 512000; -- 512 KB.
PRAGMA temp_store = MEMORY;
PRAGMA auto_vacuum = INCREMENTAL;
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.

Gonna want comments about why these values.

",
)?;

// Set hardening flags.
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.

Having now read the doc, I'd group this ("ordinary SQL able to deliberately corrupt the db") with misfeatures


self.conn
.read(|conn| {
let mut stmt = conn.prepare_cached(iter_sql).unwrap();
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.

unguarded and uncommented unwrap


Result::<(), ()>::Ok(())
})
.unwrap()
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.

unguarded and uncommented unwrap

if let Err(e) =
self.record_per_lifetime(tx, data.inner.lifetime, ping_name, &name, value)
{
log::error!(
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.

(musing) I was gonna say "TODO: instrument this error" but I guess we have limited capacity for instrumenting the db gone awry. ...I suppose an in-memory error structure could be included in a "health" ping or something... I dunno.

lifetime TEXT NOT NULL,
labels TEXT NOT NULL, -- can't be null or ON CONFLICT won't work
value BLOB,
UNIQUE(id, ping, labels)
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.

This reflects a change in semantics, doesn't it? Previously, if a metric changed lifetime we'd store both.

Also: why are labels part of the unique constraint?

lifetime = ?1
AND ping = ?2
AND id = ?3
LIMIT 1
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'd be more tempted to allow all rows in the result and instrument an error if we got more than 1

///
/// This function will **not** panic on database errors.
pub fn clear_ping_lifetime_storage(&self, storage_name: &str) -> Result<()> {
let clear_sql = "DELETE FROM telemetry WHERE lifetime = ?1 AND ping = ?2";
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.

Why not include Lifetime::Ping.as_str() in the statement directly?

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.

2 participants