Cache the TSInteraction insert statement for backup restore.

This commit is contained in:
Pete Walters
2024-10-24 11:54:46 -05:00
committed by GitHub
parent 9b01bd5fdd
commit e5a5f7c451
3 changed files with 151 additions and 3 deletions

View File

@@ -1128,7 +1128,8 @@ def generate_swift_extensions_for_model(clazz):
return
has_sds_superclass = clazz.has_sds_superclass()
has_remove_methods = clazz.name not in ("TSThread","TSInteraction")
has_remove_methods = clazz.name not in ("TSThread", "TSInteraction")
has_grdb_serializer = clazz.name in ("TSInteraction")
swift_filename = os.path.basename(clazz.filepath)
swift_filename = swift_filename[: swift_filename.find(".")] + "+SDS.swift"
@@ -1758,6 +1759,43 @@ extension %(class_name)s: DeepCopyable {
swift_body += """
}
}
"""
if has_grdb_serializer:
swift_body += """
// MARK: - Table Metadata
extension %sRecord {
// This defines all of the columns used in the table
// where this model (and any subclasses) are persisted.
internal func asArguments() -> StatementArguments {
let databaseValues: [DatabaseValueConvertible?] = [
""" % str(
remove_prefix_from_class_name(clazz.name)
)
def write_grdb_column_metadata(property):
# column_name = property.swift_identifier()
column_name = property.column_name()
return """ %s,
""" % (
str(column_name)
)
for property in sds_properties:
if property.name != "id":
swift_body += write_grdb_column_metadata(property)
if len(record_properties) > 0:
for property in record_properties:
swift_body += write_grdb_column_metadata(property)
swift_body += """
]
return StatementArguments(databaseValues)
}
}
"""
if not has_sds_superclass:
@@ -1817,7 +1855,9 @@ extension %sSerializer {
SDSTableMetadata(
tableName: "%s",
columns: [
""" % (database_table_name,)
""" % (
database_table_name,
)
swift_body += "\n".join(
[
" %sColumn," % str(column_property_name)

View File

@@ -30,6 +30,9 @@ public final class MessageBackupInteractionStore {
{}
}
// Even generating the sql string itself is expensive when multiplied by 200k messages.
// So we generate the string once and cache it (on top of caching the Statement)
private var cachedSQL: String?
func insert(
_ interaction: TSInteraction,
in thread: MessageBackup.ChatThread,
@@ -67,7 +70,23 @@ public final class MessageBackupInteractionStore {
// and restore, we'll only send back a Null message. (Until such a day
// when resends use the interactions table and not MSL at all).
try interaction.asRecord().insert(context.tx.databaseConnection)
let sql: String
if let cachedSQL {
sql = cachedSQL
} else {
let columnsSQL = InteractionRecord.CodingKeys.allCases.filter({ $0 != .id }).map(\.name).joined(separator: ", ")
let valuesSQL = InteractionRecord.CodingKeys.allCases.filter({ $0 != .id }).map({ _ in "?" }).joined(separator: ", ")
sql = """
INSERT INTO \(InteractionRecord.databaseTableName) (\(columnsSQL)) \
VALUES (\(valuesSQL))
"""
cachedSQL = sql
}
let statement = try context.tx.databaseConnection.cachedStatement(sql: sql)
try statement.setUncheckedArguments((interaction.asRecord() as! InteractionRecord).asArguments())
try statement.execute()
interaction.updateRowId(context.tx.databaseConnection.lastInsertedRowID)
guard let interactionRowId = interaction.sqliteRowId else {
throw OWSAssertionError("Missing row id after insertion!")

View File

@@ -6212,6 +6212,95 @@ extension TSInteraction: DeepCopyable {
// MARK: - Table Metadata
extension InteractionRecord {
// This defines all of the columns used in the table
// where this model (and any subclasses) are persisted.
internal func asArguments() -> StatementArguments {
let databaseValues: [DatabaseValueConvertible?] = [
recordType,
uniqueId,
receivedAtTimestamp,
timestamp,
threadUniqueId,
attachmentIds,
authorId,
authorPhoneNumber,
authorUUID,
body,
callType,
configurationDurationSeconds,
configurationIsEnabled,
contactShare,
createdByRemoteName,
createdInExistingGroup,
customMessage,
envelopeData,
errorType,
expireStartedAt,
expiresAt,
expiresInSeconds,
groupMetaMessage,
hasLegacyMessageState,
hasSyncedTranscript,
wasNotCreatedLocally,
isLocalChange,
isViewOnceComplete,
isViewOnceMessage,
isVoiceMessage,
legacyMessageState,
legacyWasDelivered,
linkPreview,
messageId,
messageSticker,
messageType,
mostRecentFailureText,
preKeyBundle,
protocolVersion,
quotedMessage,
read,
recipientAddress,
recipientAddressStates,
sender,
serverTimestamp,
deprecated_sourceDeviceId,
storedMessageState,
storedShouldStartExpireTimer,
unregisteredAddress,
verificationState,
wasReceivedByUD,
infoMessageUserInfo,
wasRemotelyDeleted,
bodyRanges,
offerType,
serverDeliveryTimestamp,
eraId,
hasEnded,
creatorUuid,
joinedMemberUuids,
wasIdentityVerified,
paymentCancellation,
paymentNotification,
paymentRequest,
viewed,
serverGuid,
storyAuthorUuidString,
storyTimestamp,
isGroupStoryReply,
storyReactionEmoji,
giftBadge,
editState,
archivedPaymentInfo,
expireTimerVersion,
isSmsMessageRestoredFromBackup,
]
return StatementArguments(databaseValues)
}
}
// MARK: - Table Metadata
extension TSInteractionSerializer {
// This defines all of the columns used in the table