DB optimizations
This commit is contained in:
parent
7172dc5e45
commit
6d3c66d251
|
@ -12,29 +12,29 @@ public enum IdentityDatabaseError: Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct IdentityDatabase {
|
public struct IdentityDatabase {
|
||||||
private let databaseQueue: DatabaseQueue
|
private let databaseWriter: DatabaseWriter
|
||||||
|
|
||||||
public init(inMemory: Bool, keychain: Keychain.Type) throws {
|
public init(inMemory: Bool, keychain: Keychain.Type) throws {
|
||||||
if inMemory {
|
if inMemory {
|
||||||
databaseQueue = DatabaseQueue()
|
databaseWriter = DatabaseQueue()
|
||||||
} else {
|
} else {
|
||||||
let path = try FileManager.default.databaseDirectoryURL(name: Self.name).path
|
let path = try FileManager.default.databaseDirectoryURL(name: Self.name).path
|
||||||
var configuration = Configuration()
|
var configuration = Configuration()
|
||||||
|
|
||||||
configuration.prepareDatabase {
|
configuration.prepareDatabase {
|
||||||
try $0.usePassphrase(try Secrets.databaseKey(identityID: nil, keychain: keychain))
|
try $0.usePassphrase(Secrets.databaseKey(identityID: nil, keychain: keychain))
|
||||||
}
|
}
|
||||||
|
|
||||||
databaseQueue = try DatabaseQueue(path: path, configuration: configuration)
|
databaseWriter = try DatabasePool(path: path, configuration: configuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
try Self.migrate(databaseQueue)
|
try migrate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension IdentityDatabase {
|
public extension IdentityDatabase {
|
||||||
func createIdentity(id: UUID, url: URL, authenticated: Bool, pending: Bool) -> AnyPublisher<Never, Error> {
|
func createIdentity(id: UUID, url: URL, authenticated: Bool, pending: Bool) -> AnyPublisher<Never, Error> {
|
||||||
databaseQueue.writePublisher(
|
databaseWriter.writePublisher(
|
||||||
updates: IdentityRecord(
|
updates: IdentityRecord(
|
||||||
id: id,
|
id: id,
|
||||||
url: url,
|
url: url,
|
||||||
|
@ -51,13 +51,13 @@ public extension IdentityDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteIdentity(id: UUID) -> AnyPublisher<Never, Error> {
|
func deleteIdentity(id: UUID) -> AnyPublisher<Never, Error> {
|
||||||
databaseQueue.writePublisher(updates: IdentityRecord.filter(Column("id") == id).deleteAll)
|
databaseWriter.writePublisher(updates: IdentityRecord.filter(Column("id") == id).deleteAll)
|
||||||
.ignoreOutput()
|
.ignoreOutput()
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateLastUsedAt(identityID: UUID) -> AnyPublisher<Never, Error> {
|
func updateLastUsedAt(identityID: UUID) -> AnyPublisher<Never, Error> {
|
||||||
databaseQueue.writePublisher {
|
databaseWriter.writePublisher {
|
||||||
try IdentityRecord
|
try IdentityRecord
|
||||||
.filter(Column("id") == identityID)
|
.filter(Column("id") == identityID)
|
||||||
.updateAll($0, Column("lastUsedAt").set(to: Date()))
|
.updateAll($0, Column("lastUsedAt").set(to: Date()))
|
||||||
|
@ -67,7 +67,7 @@ public extension IdentityDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateInstance(_ instance: Instance, forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
|
func updateInstance(_ instance: Instance, forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
|
||||||
databaseQueue.writePublisher {
|
databaseWriter.writePublisher {
|
||||||
try Identity.Instance(
|
try Identity.Instance(
|
||||||
uri: instance.uri,
|
uri: instance.uri,
|
||||||
streamingAPI: instance.urls.streamingApi,
|
streamingAPI: instance.urls.streamingApi,
|
||||||
|
@ -83,7 +83,7 @@ public extension IdentityDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateAccount(_ account: Account, forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
|
func updateAccount(_ account: Account, forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
|
||||||
databaseQueue.writePublisher(
|
databaseWriter.writePublisher(
|
||||||
updates: Identity.Account(
|
updates: Identity.Account(
|
||||||
id: account.id,
|
id: account.id,
|
||||||
identityID: identityID,
|
identityID: identityID,
|
||||||
|
@ -101,7 +101,7 @@ public extension IdentityDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func confirmIdentity(id: UUID) -> AnyPublisher<Never, Error> {
|
func confirmIdentity(id: UUID) -> AnyPublisher<Never, Error> {
|
||||||
databaseQueue.writePublisher {
|
databaseWriter.writePublisher {
|
||||||
try IdentityRecord
|
try IdentityRecord
|
||||||
.filter(Column("id") == id)
|
.filter(Column("id") == id)
|
||||||
.updateAll($0, Column("pending").set(to: false))
|
.updateAll($0, Column("pending").set(to: false))
|
||||||
|
@ -112,7 +112,7 @@ public extension IdentityDatabase {
|
||||||
|
|
||||||
func updatePreferences(_ preferences: Mastodon.Preferences,
|
func updatePreferences(_ preferences: Mastodon.Preferences,
|
||||||
forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
|
forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
|
||||||
databaseQueue.writePublisher {
|
databaseWriter.writePublisher {
|
||||||
guard let storedPreferences = try IdentityRecord.filter(Column("id") == identityID)
|
guard let storedPreferences = try IdentityRecord.filter(Column("id") == identityID)
|
||||||
.fetchOne($0)?
|
.fetchOne($0)?
|
||||||
.preferences else {
|
.preferences else {
|
||||||
|
@ -127,7 +127,7 @@ public extension IdentityDatabase {
|
||||||
|
|
||||||
func updatePreferences(_ preferences: Identity.Preferences,
|
func updatePreferences(_ preferences: Identity.Preferences,
|
||||||
forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
|
forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
|
||||||
databaseQueue.writePublisher(updates: Self.writePreferences(preferences, id: identityID))
|
databaseWriter.writePublisher(updates: Self.writePreferences(preferences, id: identityID))
|
||||||
.ignoreOutput()
|
.ignoreOutput()
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,7 @@ public extension IdentityDatabase {
|
||||||
func updatePushSubscription(alerts: PushSubscription.Alerts,
|
func updatePushSubscription(alerts: PushSubscription.Alerts,
|
||||||
deviceToken: Data? = nil,
|
deviceToken: Data? = nil,
|
||||||
forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
|
forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
|
||||||
databaseQueue.writePublisher {
|
databaseWriter.writePublisher {
|
||||||
let data = try IdentityRecord.databaseJSONEncoder(for: "pushSubscriptionAlerts").encode(alerts)
|
let data = try IdentityRecord.databaseJSONEncoder(for: "pushSubscriptionAlerts").encode(alerts)
|
||||||
|
|
||||||
try IdentityRecord
|
try IdentityRecord
|
||||||
|
@ -159,7 +159,7 @@ public extension IdentityDatabase {
|
||||||
.identityResultRequest
|
.identityResultRequest
|
||||||
.fetchOne)
|
.fetchOne)
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.publisher(in: databaseQueue, scheduling: immediate ? .immediate : .async(onQueue: .main))
|
.publisher(in: databaseWriter, scheduling: immediate ? .immediate : .async(onQueue: .main))
|
||||||
.tryMap {
|
.tryMap {
|
||||||
guard let result = $0 else { throw IdentityDatabaseError.identityNotFound }
|
guard let result = $0 else { throw IdentityDatabaseError.identityNotFound }
|
||||||
|
|
||||||
|
@ -175,7 +175,7 @@ public extension IdentityDatabase {
|
||||||
.identityResultRequest
|
.identityResultRequest
|
||||||
.fetchAll)
|
.fetchAll)
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.publisher(in: databaseQueue)
|
.publisher(in: databaseWriter)
|
||||||
.map { $0.map(Identity.init(result:)) }
|
.map { $0.map(Identity.init(result:)) }
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
@ -189,7 +189,7 @@ public extension IdentityDatabase {
|
||||||
.limit(9)
|
.limit(9)
|
||||||
.fetchAll)
|
.fetchAll)
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.publisher(in: databaseQueue)
|
.publisher(in: databaseWriter)
|
||||||
.map { $0.map(Identity.init(result:)) }
|
.map { $0.map(Identity.init(result:)) }
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
@ -197,12 +197,12 @@ public extension IdentityDatabase {
|
||||||
func immediateMostRecentlyUsedIdentityIDObservation() -> AnyPublisher<UUID?, Error> {
|
func immediateMostRecentlyUsedIdentityIDObservation() -> AnyPublisher<UUID?, Error> {
|
||||||
ValueObservation.tracking(IdentityRecord.select(Column("id")).order(Column("lastUsedAt").desc).fetchOne)
|
ValueObservation.tracking(IdentityRecord.select(Column("id")).order(Column("lastUsedAt").desc).fetchOne)
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.publisher(in: databaseQueue, scheduling: .immediate)
|
.publisher(in: databaseWriter, scheduling: .immediate)
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
func identitiesWithOutdatedDeviceTokens(deviceToken: Data) -> AnyPublisher<[Identity], Error> {
|
func identitiesWithOutdatedDeviceTokens(deviceToken: Data) -> AnyPublisher<[Identity], Error> {
|
||||||
databaseQueue.readPublisher(
|
databaseWriter.readPublisher(
|
||||||
value: IdentityRecord
|
value: IdentityRecord
|
||||||
.order(Column("lastUsedAt").desc)
|
.order(Column("lastUsedAt").desc)
|
||||||
.identityResultRequest
|
.identityResultRequest
|
||||||
|
@ -214,9 +214,9 @@ public extension IdentityDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension IdentityDatabase {
|
private extension IdentityDatabase {
|
||||||
private static let name = "Identity"
|
static let name = "Identity"
|
||||||
|
|
||||||
private static func writePreferences(_ preferences: Identity.Preferences, id: UUID) -> (Database) throws -> Void {
|
static func writePreferences(_ preferences: Identity.Preferences, id: UUID) -> (Database) throws -> Void {
|
||||||
{
|
{
|
||||||
let data = try IdentityRecord.databaseJSONEncoder(for: "preferences").encode(preferences)
|
let data = try IdentityRecord.databaseJSONEncoder(for: "preferences").encode(preferences)
|
||||||
|
|
||||||
|
@ -226,7 +226,7 @@ private extension IdentityDatabase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func migrate(_ writer: DatabaseWriter) throws {
|
func migrate() throws {
|
||||||
var migrator = DatabaseMigrator()
|
var migrator = DatabaseMigrator()
|
||||||
|
|
||||||
migrator.registerMigration("createIdentities") { db in
|
migrator.registerMigration("createIdentities") { db in
|
||||||
|
@ -238,13 +238,12 @@ private extension IdentityDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
try db.create(table: "identityRecord", ifNotExists: true) { t in
|
try db.create(table: "identityRecord", ifNotExists: true) { t in
|
||||||
t.column("id", .text).notNull().primaryKey(onConflict: .replace)
|
t.column("id", .text).indexed().notNull().primaryKey(onConflict: .replace)
|
||||||
t.column("url", .text).notNull()
|
t.column("url", .text).notNull()
|
||||||
t.column("authenticated", .boolean).notNull()
|
t.column("authenticated", .boolean).notNull()
|
||||||
t.column("pending", .boolean).notNull()
|
t.column("pending", .boolean).notNull()
|
||||||
t.column("lastUsedAt", .datetime).notNull()
|
t.column("lastUsedAt", .datetime).notNull()
|
||||||
t.column("instanceURI", .text)
|
t.column("instanceURI", .text)
|
||||||
.indexed()
|
|
||||||
.references("instance", column: "uri")
|
.references("instance", column: "uri")
|
||||||
t.column("preferences", .blob).notNull()
|
t.column("preferences", .blob).notNull()
|
||||||
t.column("pushSubscriptionAlerts", .blob).notNull()
|
t.column("pushSubscriptionAlerts", .blob).notNull()
|
||||||
|
@ -253,9 +252,7 @@ private extension IdentityDatabase {
|
||||||
|
|
||||||
try db.create(table: "account", ifNotExists: true) { t in
|
try db.create(table: "account", ifNotExists: true) { t in
|
||||||
t.column("id", .text).notNull().primaryKey(onConflict: .replace)
|
t.column("id", .text).notNull().primaryKey(onConflict: .replace)
|
||||||
t.column("identityID", .text)
|
t.column("identityID", .text).notNull()
|
||||||
.notNull()
|
|
||||||
.indexed()
|
|
||||||
.references("identityRecord", column: "id", onDelete: .cascade)
|
.references("identityRecord", column: "id", onDelete: .cascade)
|
||||||
t.column("username", .text).notNull()
|
t.column("username", .text).notNull()
|
||||||
t.column("displayName", .text).notNull()
|
t.column("displayName", .text).notNull()
|
||||||
|
@ -268,6 +265,6 @@ private extension IdentityDatabase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try migrator.migrate(writer)
|
try migrator.migrate(databaseWriter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue