Batching
This commit is contained in:
@@ -448,6 +448,26 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
|
|
||||||
// --- Bills logic ---
|
// --- Bills logic ---
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs [block] as one database transaction.
|
||||||
|
*
|
||||||
|
* Every write here commits on its own otherwise, which for a sync means a committed
|
||||||
|
* transaction per bill and another per ower - the dominant cost of a large project's first
|
||||||
|
* sync. Callers batch in chunks rather than wrapping everything: a sync interrupted halfway
|
||||||
|
* then keeps the chunks it already committed instead of rolling the lot back.
|
||||||
|
*/
|
||||||
|
fun <T> inTransaction(block: () -> T): T {
|
||||||
|
val db = writableDatabase
|
||||||
|
db.beginTransaction()
|
||||||
|
try {
|
||||||
|
val result = block()
|
||||||
|
db.setTransactionSuccessful()
|
||||||
|
return result
|
||||||
|
} finally {
|
||||||
|
db.endTransaction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun addBill(b: DBBill): Long {
|
fun addBill(b: DBBill): Long {
|
||||||
val db = writableDatabase
|
val db = writableDatabase
|
||||||
val values = ContentValues()
|
val values = ContentValues()
|
||||||
@@ -627,11 +647,25 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
val cursor = db.query(table_bills, columnsBills, selection, selectionArgs, null, null, orderBy)
|
val cursor = db.query(table_bills, columnsBills, selection, selectionArgs, null, null, orderBy)
|
||||||
val bills: MutableList<DBBill> = ArrayList()
|
val bills: MutableList<DBBill> = ArrayList()
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
val bill = getBillFromCursor(cursor)
|
bills.add(getBillFromCursor(cursor))
|
||||||
bill.billOwers = getBillowersOfBill(bill.id)
|
|
||||||
bills.add(bill)
|
|
||||||
}
|
}
|
||||||
cursor.close()
|
cursor.close()
|
||||||
|
if (bills.isEmpty()) return bills
|
||||||
|
|
||||||
|
// Every ower of the matched bills in one query, keyed back to its bill.
|
||||||
|
//
|
||||||
|
// Fetching them per bill meant a project with thousands of bills issued thousands of
|
||||||
|
// queries on every list refresh - and that refresh runs on resume, on switching project,
|
||||||
|
// and after each sync. The bill ids are re-selected as a subquery rather than bound as
|
||||||
|
// arguments, which would blow past SQLite's limit on bound variables for a large project.
|
||||||
|
val owersByBill = getBillOwersCustom(
|
||||||
|
"$key_billId IN (SELECT $key_id FROM $table_bills WHERE $selection)",
|
||||||
|
selectionArgs,
|
||||||
|
null
|
||||||
|
).groupBy { it.billId }
|
||||||
|
for (bill in bills) {
|
||||||
|
bill.billOwers = owersByBill[bill.id].orEmpty()
|
||||||
|
}
|
||||||
return bills
|
return bills
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -896,6 +896,18 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
private fun applyRemoteBills(
|
private fun applyRemoteBills(
|
||||||
remoteBills: List<DBBill>,
|
remoteBills: List<DBBill>,
|
||||||
localBillsByRemoteId: Map<Long, DBBill>
|
localBillsByRemoteId: Map<Long, DBBill>
|
||||||
|
) {
|
||||||
|
// Committed in chunks: one transaction per chunk turns a per-row commit into one write
|
||||||
|
// for the whole batch, while still leaving finished chunks on disk if the sync is cut
|
||||||
|
// short - a project part-way through is far better than one that rolled back.
|
||||||
|
remoteBills.chunked(BILL_APPLY_CHUNK).forEach { chunk ->
|
||||||
|
dbHelper.inTransaction { applyBillChunk(chunk, localBillsByRemoteId) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyBillChunk(
|
||||||
|
remoteBills: List<DBBill>,
|
||||||
|
localBillsByRemoteId: Map<Long, DBBill>
|
||||||
) {
|
) {
|
||||||
for (remoteBill in remoteBills) {
|
for (remoteBill in remoteBills) {
|
||||||
val localBill = localBillsByRemoteId[remoteBill.remoteId]
|
val localBill = localBillsByRemoteId[remoteBill.remoteId]
|
||||||
@@ -1792,6 +1804,9 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
*/
|
*/
|
||||||
private const val UNCHANGED_RUN_TO_SETTLE = 25
|
private const val UNCHANGED_RUN_TO_SETTLE = 25
|
||||||
|
|
||||||
|
/** Bills written per transaction while applying a pull. */
|
||||||
|
private const val BILL_APPLY_CHUNK = 500
|
||||||
|
|
||||||
private var instance: CowspentServerSyncHelper? = null
|
private var instance: CowspentServerSyncHelper? = null
|
||||||
private val projectIdsToSync: MutableList<Long> = ArrayList()
|
private val projectIdsToSync: MutableList<Long> = ArrayList()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user