Skip to content

Commit

Permalink
Protect db connection by mutex
Browse files Browse the repository at this point in the history
  • Loading branch information
bubelov committed May 18, 2024
1 parent 1d60da0 commit 9107b88
Show file tree
Hide file tree
Showing 13 changed files with 756 additions and 721 deletions.
4 changes: 2 additions & 2 deletions app/src/main/kotlin/app/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import area.AreasModel
import area.AreasRepo
import conf.ConfQueries
import conf.ConfRepo
import db.openDbConnection
import db.Database
import delivery.DeliveryModel
import element.ElementQueries
import element.ElementsRepo
Expand All @@ -36,7 +36,7 @@ import user.UsersModel
import user.UsersRepo

val appModule = module {
single { openDbConnection(get()) }
single { Database(get()) }

single { ApiImpl() }.bind(Api::class)

Expand Down
157 changes: 81 additions & 76 deletions app/src/main/kotlin/area/AreaQueries.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package area

import androidx.sqlite.SQLiteConnection
import androidx.sqlite.execSQL
import androidx.sqlite.use
import db.Database
import db.getJsonObject
import db.getZonedDateTime
import java.time.ZonedDateTime

class AreaQueries(private val conn: SQLiteConnection) {
class AreaQueries(private val db: Database) {

companion object {
const val CREATE_TABLE = """
Expand All @@ -20,113 +19,119 @@ class AreaQueries(private val conn: SQLiteConnection) {
}

fun insertOrReplace(areas: List<Area>) {
conn.execSQL("BEGIN IMMEDIATE TRANSACTION")

try {
areas.forEach { insertOrReplace(it) }
conn.execSQL("END TRANSACTION")
} catch (t: Throwable) {
conn.execSQL("ROLLBACK TRANSACTION")
}
}

fun insertOrReplace(area: Area) {
conn.prepare("INSERT OR REPLACE INTO area(id, tags, updated_at) VALUES(?1, ?2, ?3)").use {
it.bindLong(1, area.id)
it.bindText(2, area.tags.toString())
it.bindText(3, area.updatedAt.toString())
it.step()
db.transaction { conn ->
areas.forEach { area ->
conn.prepare("INSERT OR REPLACE INTO area(id, tags, updated_at) VALUES(?1, ?2, ?3)")
.use {
it.bindLong(1, area.id)
it.bindText(2, area.tags.toString())
it.bindText(3, area.updatedAt.toString())
it.step()
}
}
}
}

fun selectById(id: Long): Area? {
return conn.prepare("SELECT id, tags, updated_at FROM area WHERE id = ?1").use {
it.bindLong(1, id)
return db.withConn { conn ->
conn.prepare("SELECT id, tags, updated_at FROM area WHERE id = ?1").use {
it.bindLong(1, id)

if (it.step()) {
Area(
id = it.getLong(0),
tags = it.getJsonObject(1),
updatedAt = it.getZonedDateTime(2),
)
} else {
null
if (it.step()) {
Area(
id = it.getLong(0),
tags = it.getJsonObject(1),
updatedAt = it.getZonedDateTime(2),
)
} else {
null
}
}
}
}

fun selectByType(type: String): List<Area> {
return conn.prepare(
"""
SELECT id, tags, updated_at
FROM area
WHERE json_extract(tags, '$.type') = ?1
"""
).use {
it.bindText(1, type)
return db.withConn { conn ->
conn.prepare(
"""
SELECT id, tags, updated_at
FROM area
WHERE json_extract(tags, '$.type') = ?1
"""
).use {
it.bindText(1, type)

buildList {
while (it.step()) {
add(
Area(
id = it.getLong(0),
tags = it.getJsonObject(1),
updatedAt = it.getZonedDateTime(2),
buildList {
while (it.step()) {
add(
Area(
id = it.getLong(0),
tags = it.getJsonObject(1),
updatedAt = it.getZonedDateTime(2),
)
)
)
}
}
}
}
}

fun selectMaxUpdatedAt(): ZonedDateTime? {
return conn.prepare("SELECT max(updated_at) FROM area").use {
if (it.step()) {
it.getZonedDateTime(0)
} else {
null
return db.withConn { conn ->
conn.prepare("SELECT max(updated_at) FROM area").use {
if (it.step()) {
it.getZonedDateTime(0)
} else {
null
}
}
}
}

fun selectMeetups(): List<Meetup> {
return conn.prepare(
"""
SELECT
json_extract(tags, '$.meetup_lat') AS lat,
json_extract(tags, '$.meetup_lon') AS lon,
id
FROM area
WHERE
lat IS NOT NULL
AND lon IS NOT NULL
"""
).use {
buildList {
while (it.step()) {
add(
Meetup(
lat = it.getDouble(0),
lon = it.getDouble(1),
areaId = it.getLong(2),
return db.withConn { conn ->
conn.prepare(
"""
SELECT
json_extract(tags, '$.meetup_lat') AS lat,
json_extract(tags, '$.meetup_lon') AS lon,
id
FROM area
WHERE
lat IS NOT NULL
AND lon IS NOT NULL
"""
).use {
buildList {
while (it.step()) {
add(
Meetup(
lat = it.getDouble(0),
lon = it.getDouble(1),
areaId = it.getLong(2),
)
)
)
}
}
}
}
}

fun selectCount(): Long {
return conn.prepare("SELECT count(*) FROM area").use {
it.step()
it.getLong(0)
return db.withConn { conn ->
conn.prepare("SELECT count(*) FROM area").use {
it.step()
it.getLong(0)
}
}
}

fun deleteById(id: Long) {
conn.prepare("DELETE FROM area WHERE id = ?1").use {
it.bindLong(1, id)
it.step()
db.withConn { conn ->
conn.prepare("DELETE FROM area WHERE id = ?1").use {
it.bindLong(1, id)
it.step()
}
}
}
}
2 changes: 1 addition & 1 deletion app/src/main/kotlin/area/AreasRepo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class AreasRepo(
updatedItems++
}

queries.insertOrReplace(it.toArea())
queries.insertOrReplace(listOf(it.toArea()))
} else {
if (cached == null) {
// Already evicted from cache, nothing to do here
Expand Down
100 changes: 48 additions & 52 deletions app/src/main/kotlin/conf/ConfQueries.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package conf

import androidx.sqlite.SQLiteConnection
import androidx.sqlite.execSQL
import androidx.sqlite.use
import db.Database
import db.getZonedDateTime

class ConfQueries(private val conn: SQLiteConnection) {
class ConfQueries(private val db: Database) {

companion object {
const val CREATE_TABLE = """
Expand All @@ -24,27 +24,25 @@ class ConfQueries(private val conn: SQLiteConnection) {
}

fun insertOrReplace(conf: Conf) {
conn.execSQL("BEGIN IMMEDIATE TRANSACTION")

try {
db.transaction { conn ->
conn.execSQL("DELETE FROM conf")

conn.prepare(
"""
INSERT
INTO conf (
last_sync_date,
viewport_north_lat,
viewport_east_lon,
viewport_south_lat,
viewport_west_lon,
show_atms,
show_osm_attribution,
show_sync_summary,
show_all_new_elements
)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9);
"""
INSERT
INTO conf (
last_sync_date,
viewport_north_lat,
viewport_east_lon,
viewport_south_lat,
viewport_west_lon,
show_atms,
show_osm_attribution,
show_sync_summary,
show_all_new_elements
)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)
"""
).use {
it.bindText(1, conf.lastSyncDate?.toString() ?: "")
it.bindDouble(2, conf.viewportNorthLat)
Expand All @@ -56,43 +54,41 @@ class ConfQueries(private val conn: SQLiteConnection) {
it.bindLong(8, if (conf.showSyncSummary) 1 else 0)
it.bindLong(9, if (conf.showAllNewElements) 1 else 0)
}

conn.execSQL("END TRANSACTION")
} catch (t: Throwable) {
conn.execSQL("ROLLBACK TRANSACTION")
}
}

fun select(): Conf? {
return conn.prepare(
"""
SELECT
last_sync_date,
viewport_north_lat,
viewport_east_lon,
viewport_south_lat,
viewport_west_lon,
show_atms,
show_osm_attribution,
show_sync_summary,
show_all_new_elements
FROM conf
"""
).use {
if (it.step()) {
Conf(
lastSyncDate = it.getZonedDateTime(0),
viewportNorthLat = it.getDouble(1),
viewportEastLon = it.getDouble(2),
viewportSouthLat = it.getDouble(3),
viewportWestLon = it.getDouble(4),
showAtms = it.getBoolean(5),
showOsmAttribution = it.getBoolean(6),
showSyncSummary = it.getBoolean(7),
showAllNewElements = it.getBoolean(8),
)
} else {
null
return db.withConn { conn ->
conn.prepare(
"""
SELECT
last_sync_date,
viewport_north_lat,
viewport_east_lon,
viewport_south_lat,
viewport_west_lon,
show_atms,
show_osm_attribution,
show_sync_summary,
show_all_new_elements
FROM conf
"""
).use {
if (it.step()) {
Conf(
lastSyncDate = it.getZonedDateTime(0),
viewportNorthLat = it.getDouble(1),
viewportEastLon = it.getDouble(2),
viewportSouthLat = it.getDouble(3),
viewportWestLon = it.getDouble(4),
showAtms = it.getBoolean(5),
showOsmAttribution = it.getBoolean(6),
showSyncSummary = it.getBoolean(7),
showAllNewElements = it.getBoolean(8),
)
} else {
null
}
}
}
}
Expand Down
Loading

0 comments on commit 9107b88

Please sign in to comment.