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/db/database-error.vala
|
||||
engine/db/db.vala
|
||||
engine/db/db-connection.vala
|
||||
engine/db/db-context.vala
|
||||
engine/db/db-database.vala
|
||||
engine/db/db-database-error.vala
|
||||
engine/db/db-result.vala
|
||||
engine/db/db-statement.vala
|
||||
engine/db/db-synchronous-mode.vala
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
public errordomain DatabaseError {
|
||||
public errordomain Geary.DatabaseError {
|
||||
GENERAL,
|
||||
OPEN_REQUIRED,
|
||||
BUSY,
|
||||
|
|
@ -40,6 +40,7 @@ public class Geary.Db.Database : Geary.Db.Context {
|
|||
private Connection? master_connection = null;
|
||||
private int outstanding_async_jobs = 0;
|
||||
private ThreadPool<TransactionAsyncJob>? thread_pool = null;
|
||||
private Gee.LinkedList<Connection>? cx_pool = null;
|
||||
private unowned PrepareConnection? prepare_cb = null;
|
||||
|
||||
public Database(File db_file) {
|
||||
|
|
@ -76,9 +77,13 @@ public class Geary.Db.Database : Geary.Db.Context {
|
|||
}
|
||||
|
||||
if (threadsafe()) {
|
||||
assert(thread_pool == null);
|
||||
thread_pool = new ThreadPool<TransactionAsyncJob>.with_owned_data(on_async_job,
|
||||
DEFAULT_MAX_CONCURRENCY, true);
|
||||
if (thread_pool == null) {
|
||||
thread_pool = new ThreadPool<TransactionAsyncJob>.with_owned_data(on_async_job,
|
||||
DEFAULT_MAX_CONCURRENCY, true);
|
||||
}
|
||||
|
||||
if (cx_pool == null)
|
||||
cx_pool = new Gee.LinkedList<Connection>();
|
||||
} else {
|
||||
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
|
||||
master_connection = null;
|
||||
|
||||
// As per the contract above, can't simply drop the thread and connection pools; that would
|
||||
// be bad.
|
||||
|
||||
is_open = false;
|
||||
}
|
||||
|
||||
|
|
@ -220,23 +228,37 @@ public class Geary.Db.Database : Geary.Db.Context {
|
|||
|
||||
// This method must be thread-safe.
|
||||
private void on_async_job(owned TransactionAsyncJob job) {
|
||||
// create connection for this thread
|
||||
// TODO: Use connection pool -- *never* use master connection
|
||||
// go to connection pool before creating a connection -- *never* use master connection for
|
||||
// threaded operations
|
||||
Connection? cx = null;
|
||||
try {
|
||||
cx = open_connection();
|
||||
} catch (Error err) {
|
||||
debug("Warning: unable to open database connection to %s, cancelling AsyncJob: %s",
|
||||
db_file.get_path(), err.message);
|
||||
lock (cx_pool) {
|
||||
cx = cx_pool.poll();
|
||||
}
|
||||
|
||||
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)
|
||||
job.execute(cx);
|
||||
else
|
||||
job.failed(open_err);
|
||||
|
||||
lock (outstanding_async_jobs) {
|
||||
assert(outstanding_async_jobs > 0);
|
||||
--outstanding_async_jobs;
|
||||
}
|
||||
|
||||
lock (cx_pool) {
|
||||
cx_pool.offer(cx);
|
||||
}
|
||||
}
|
||||
|
||||
public override Database? get_database() {
|
||||
|
|
|
|||
|
|
@ -42,6 +42,18 @@ private class Geary.Db.TransactionAsyncJob : Object {
|
|||
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
|
||||
// because Idle doesn't hold a ref, manually keep this object alive
|
||||
ref();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue