Database connection pooling
Reusing connections is a win with asynchronous transactions. More fine-tuning in the future of cache pages and such will help improve Geary's database performance. Also fixed a naming and namespace problem with Geary.DatabaseError and a hole where, if a connection could not be opened (for resource problems, most likely), the asynchronous transaction would never complete.
This commit is contained in:
parent
a047ceb698
commit
af9a2c7f63
4 changed files with 46 additions and 12 deletions
|
|
@ -45,11 +45,11 @@ engine/api/geary-special-folder-type.vala
|
||||||
|
|
||||||
engine/common/common-message-data.vala
|
engine/common/common-message-data.vala
|
||||||
|
|
||||||
engine/db/database-error.vala
|
|
||||||
engine/db/db.vala
|
engine/db/db.vala
|
||||||
engine/db/db-connection.vala
|
engine/db/db-connection.vala
|
||||||
engine/db/db-context.vala
|
engine/db/db-context.vala
|
||||||
engine/db/db-database.vala
|
engine/db/db-database.vala
|
||||||
|
engine/db/db-database-error.vala
|
||||||
engine/db/db-result.vala
|
engine/db/db-result.vala
|
||||||
engine/db/db-statement.vala
|
engine/db/db-statement.vala
|
||||||
engine/db/db-synchronous-mode.vala
|
engine/db/db-synchronous-mode.vala
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
* (version 2.1 or later). See the COPYING file in this distribution.
|
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public errordomain DatabaseError {
|
public errordomain Geary.DatabaseError {
|
||||||
GENERAL,
|
GENERAL,
|
||||||
OPEN_REQUIRED,
|
OPEN_REQUIRED,
|
||||||
BUSY,
|
BUSY,
|
||||||
|
|
@ -40,6 +40,7 @@ public class Geary.Db.Database : Geary.Db.Context {
|
||||||
private Connection? master_connection = null;
|
private Connection? master_connection = null;
|
||||||
private int outstanding_async_jobs = 0;
|
private int outstanding_async_jobs = 0;
|
||||||
private ThreadPool<TransactionAsyncJob>? thread_pool = null;
|
private ThreadPool<TransactionAsyncJob>? thread_pool = null;
|
||||||
|
private Gee.LinkedList<Connection>? cx_pool = null;
|
||||||
private unowned PrepareConnection? prepare_cb = null;
|
private unowned PrepareConnection? prepare_cb = null;
|
||||||
|
|
||||||
public Database(File db_file) {
|
public Database(File db_file) {
|
||||||
|
|
@ -76,9 +77,13 @@ public class Geary.Db.Database : Geary.Db.Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (threadsafe()) {
|
if (threadsafe()) {
|
||||||
assert(thread_pool == null);
|
if (thread_pool == null) {
|
||||||
thread_pool = new ThreadPool<TransactionAsyncJob>.with_owned_data(on_async_job,
|
thread_pool = new ThreadPool<TransactionAsyncJob>.with_owned_data(on_async_job,
|
||||||
DEFAULT_MAX_CONCURRENCY, true);
|
DEFAULT_MAX_CONCURRENCY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cx_pool == null)
|
||||||
|
cx_pool = new Gee.LinkedList<Connection>();
|
||||||
} else {
|
} else {
|
||||||
warning("SQLite not thread-safe: asynchronous queries will not be available");
|
warning("SQLite not thread-safe: asynchronous queries will not be available");
|
||||||
}
|
}
|
||||||
|
|
@ -101,6 +106,9 @@ public class Geary.Db.Database : Geary.Db.Context {
|
||||||
// drop the master connection, which holds a ref back to this object
|
// drop the master connection, which holds a ref back to this object
|
||||||
master_connection = null;
|
master_connection = null;
|
||||||
|
|
||||||
|
// As per the contract above, can't simply drop the thread and connection pools; that would
|
||||||
|
// be bad.
|
||||||
|
|
||||||
is_open = false;
|
is_open = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -220,23 +228,37 @@ public class Geary.Db.Database : Geary.Db.Context {
|
||||||
|
|
||||||
// This method must be thread-safe.
|
// This method must be thread-safe.
|
||||||
private void on_async_job(owned TransactionAsyncJob job) {
|
private void on_async_job(owned TransactionAsyncJob job) {
|
||||||
// create connection for this thread
|
// go to connection pool before creating a connection -- *never* use master connection for
|
||||||
// TODO: Use connection pool -- *never* use master connection
|
// threaded operations
|
||||||
Connection? cx = null;
|
Connection? cx = null;
|
||||||
try {
|
lock (cx_pool) {
|
||||||
cx = open_connection();
|
cx = cx_pool.poll();
|
||||||
} catch (Error err) {
|
}
|
||||||
debug("Warning: unable to open database connection to %s, cancelling AsyncJob: %s",
|
|
||||||
db_file.get_path(), err.message);
|
Error? open_err = null;
|
||||||
|
if (cx == null) {
|
||||||
|
try {
|
||||||
|
cx = open_connection();
|
||||||
|
} catch (Error err) {
|
||||||
|
open_err = err;
|
||||||
|
debug("Warning: unable to open database connection to %s, cancelling AsyncJob: %s",
|
||||||
|
db_file.get_path(), err.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cx != null)
|
if (cx != null)
|
||||||
job.execute(cx);
|
job.execute(cx);
|
||||||
|
else
|
||||||
|
job.failed(open_err);
|
||||||
|
|
||||||
lock (outstanding_async_jobs) {
|
lock (outstanding_async_jobs) {
|
||||||
assert(outstanding_async_jobs > 0);
|
assert(outstanding_async_jobs > 0);
|
||||||
--outstanding_async_jobs;
|
--outstanding_async_jobs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lock (cx_pool) {
|
||||||
|
cx_pool.offer(cx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Database? get_database() {
|
public override Database? get_database() {
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,18 @@ private class Geary.Db.TransactionAsyncJob : Object {
|
||||||
caught_err = err;
|
caught_err = err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
schedule_completion();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called in background thread context
|
||||||
|
internal void failed(Error err) {
|
||||||
|
// store as a caught thread to report to original caller
|
||||||
|
caught_err = err;
|
||||||
|
|
||||||
|
schedule_completion();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void schedule_completion() {
|
||||||
// notify foreground thread of completion
|
// notify foreground thread of completion
|
||||||
// because Idle doesn't hold a ref, manually keep this object alive
|
// because Idle doesn't hold a ref, manually keep this object alive
|
||||||
ref();
|
ref();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue