Data Source=app.witdbThat is a complete connection string. Everything else has a default, and a database that already exists remembers most of it.
The database remembers how it was made
A .witdb records the storage engine, the cache, the journal and the encryption it was created
with. When you open it, any setting you did not name is taken from the file.
That has two consequences worth knowing before you write a connection string in a config file.
Omitting a setting is safe. A database created with Cache=lru opens with the LRU cache from
Data Source=app.witdb, without you having to repeat it everywhere the database is opened.
Naming a setting the database disagrees with is refused rather than ignored. Opening a
Store=lsm database with Store=btree is an error, because silently giving you a different engine
than you asked for is worse than failing.
The same applies to the transaction model, and there the failure it prevents was quiet and
destructive. The MVCC store keeps every value under a versioned key and no other configuration
does, so a database written with MVCC=true, the default, and opened with MVCC=false used to open
without complaint and then report every table as missing. The rows were never lost, they were
invisible, and the natural next step of creating the schema on what looked like an empty database
wrote over one that was intact. Both directions are refused now, naming the setting.
Presence is what counts, not the value. MVCC=true written explicitly is a different thing from
leaving MVCC out, even though both end up with MVCC on.
Three settings are never restored from the file: File Locking, Synchronous Commit and
Isolation Level. A file may not quietly make a database less durable or less exclusive than the
defaults promise, for a caller who said nothing about either, so those have to be asked for every
time. Isolation is excluded for a second reason as well: it is a property of a session rather than
of the data.
Core
| Parameter | Values | Default |
|---|---|---|
Data Source |
A path, or :memory: |
required |
Mode |
ReadWriteCreate, ReadWrite, ReadOnly, Memory |
ReadWriteCreate |
Store |
btree, lsm, inmemory |
btree |
Connection Timeout |
Seconds to wait for the database to open | 30 |
Default Timeout |
Seconds a command runs before it is cancelled | 30 |
Pooling |
true, false |
false |
Min Pool Size, Max Pool Size |
Connections held in the pool | 1, 100 |
Data Source points at a file for the B+Tree store and at a directory for LSM.
Mode=ReadWriteCreate creates the database when it is missing, and it is the default.
Mode=ReadWrite and Mode=ReadOnly both mean open an existing one, and both fail when the path is
not there, naming the mode and pointing at ReadWriteCreate. A mistyped path is an error rather
than an empty database.
Connection Timeout is how long to wait for the file lock, not how long to wait for a query. A
bounded wait rather than a single attempt is deliberate: restarting a host overlaps the outgoing
process with the incoming one, and refusing immediately turns that window into a startup crash.
SQLite covers the same window with busy_timeout. Zero means one attempt.
Encryption
| Parameter | Values | Default |
|---|---|---|
Password |
The password | none |
Encryption |
aes-gcm, chacha20-poly1305 |
aes-gcm when a password is given |
Legacy Encryption |
true, false |
false |
Data Source=secure.witdb;Password=your-password
Data Source=secure.witdb;Encryption=chacha20-poly1305;Password=your-passwordA password with no Encryption gets AES-GCM. An Encryption with no password is refused, since it
would produce an unencrypted database that looks encrypted in the config file.
Salt and iteration count live in the file rather than in the connection string, so nothing here has to match how the database was created.
Legacy Encryption=true opens a database encrypted before 13.1.0, which the engine otherwise
refuses. Use it to get your data out and then change the password, which rewrites the database in
the current format. See encryption.
Transactions
| Parameter | Values | Default |
|---|---|---|
Transactions |
true, false |
true |
MVCC |
true, false |
true |
Isolation Level |
ReadUncommitted, ReadCommitted, RepeatableRead, Serializable, Snapshot |
ReadCommitted |
Journal |
wal, rollback |
wal |
Synchronous Commit |
true, false |
true |
File Locking |
true, false |
true |
Synchronous Commit=false moves the moment of durability to somewhere you cannot predict: a
process crash is still survivable, a power cut is not. File Locking=false drops the cross-process
guard, so a second process can open the same file. Neither is restored from the database, so both
have to be written every time you want them.
MVCC=false gives a single-writer database where one transaction holds a database-wide write lock.
It is faster when there is genuinely one writer and it takes concurrent transactions away entirely.
It also interacts badly with Store=lsm, in the other direction from what you might expect: see
storage engines.
Cache and pages
| Parameter | Values | Default |
|---|---|---|
Cache |
clock, lru |
clock |
Cache Size |
Pages held in memory | 1000 |
Page Size |
Bytes per page, a power of two from 512 to 65536 | 4096 |
Page Size is fixed when the database is created and cannot be changed afterwards. Cache Size can
differ between connections; it is memory, not layout.
Booleans
true, yes, 1 and on all mean true, and false, no, 0 and off all mean false, in any
case. A value that is none of those is an error rather than a silent false.
Keywords that no longer exist
| Removed | What to do |
|---|---|
Parallel Mode |
Remove it. The B+Tree store is always serialised, since it has no locking of its own, and the LSM store locks internally. There is nothing left to choose |
Max Writers |
Remove it. It sized the LSM write buffer, which is no longer selectable from a connection string, and which measured slower through a database anyway because the transaction layer serialises writers before they reach it |
Both are rejected with that advice rather than ignored.
Building one in code
using OutWit.Database.AdoNet;
var builder = new WitDbConnectionStringBuilder
{
DataSource = "/var/app/data.witdb",
Password = configuration["Database:Password"],
CacheSize = 5000,
IsolationLevel = WitDbIsolationLevel.Snapshot
};
using var connection = new WitDbConnection(builder.ConnectionString);It parses as well as builds, so an existing string can be read, changed and written back out.
Validate() returns the problems it can see without opening anything, which is useful at startup:
var errors = builder.Validate();
if (errors.Count > 0)
throw new InvalidOperationException(string.Join("; ", errors));Some complete ones
// Tests
Data Source=:memory:
// A desktop application's document
Data Source={appData}/MyApp/state.witdb;Password={userPassword}
// Read-heavy, with room to cache
Data Source=analytics.witdb;Mode=ReadOnly;Cache Size=20000;Cache=lru
// A service, snapshot reads
Data Source=/var/app/data.witdb;Password={secret};Isolation Level=SnapshotWhere to go next
- Storage engines, what
Storeselects - Encryption, what
PasswordandEncryptionselect - Caching, sizing
Cache Size - Transactions, what
Isolation Levelmeans