mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			119 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			119 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Swift
		
	
| //
 | |
| //  Copyright (c) 2019 Open Whisper Systems. All rights reserved.
 | |
| //
 | |
| 
 | |
| import Foundation
 | |
| 
 | |
| public class OWS110SortIdMigration: OWSDatabaseMigration {
 | |
|     // increment a similar constant for each migration.
 | |
|     @objc
 | |
|     class func migrationId() -> String {
 | |
|         // append char "x" because we want to rerun on some internal devices which
 | |
|         // have already run this migration.
 | |
|         return "110x"
 | |
|     }
 | |
| 
 | |
|     override public func runUp(completion: @escaping OWSDatabaseMigrationCompletion) {
 | |
|         Logger.debug("")
 | |
|         BenchAsync(title: "Sort Migration") { completeBenchmark in
 | |
|             self.doMigration {
 | |
|                 completeBenchmark()
 | |
|                 completion()
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private func doMigration(completion: @escaping OWSDatabaseMigrationCompletion) {
 | |
|         // TODO batch this?
 | |
|         self.dbReadWriteConnection().readWrite { transaction in
 | |
| 
 | |
|             var archivedThreads: [TSThread] = []
 | |
| 
 | |
|             // get archived threads before migration
 | |
|             TSThread.enumerateCollectionObjects(with: transaction) { (object, _) in
 | |
|                 guard let thread = object as? TSThread else {
 | |
|                     owsFailDebug("unexpected object: \(type(of: object))")
 | |
|                     return
 | |
|                 }
 | |
| 
 | |
|                 if thread.isArchivedByLegacyTimestampForSorting {
 | |
|                     archivedThreads.append(thread)
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             guard let legacySorting: YapDatabaseAutoViewTransaction = transaction.extension(TSMessageDatabaseViewExtensionName_Legacy) as? YapDatabaseAutoViewTransaction else {
 | |
|                 owsFailDebug("legacySorting was unexpectedly nil")
 | |
|                 return
 | |
|             }
 | |
| 
 | |
|             let totalCount: UInt = legacySorting.numberOfItemsInAllGroups()
 | |
|             var completedCount: UInt = 0
 | |
| 
 | |
|             var allGroups = [String]()
 | |
|             legacySorting.enumerateGroups { group, _ in
 | |
|                 allGroups.append(group)
 | |
|             }
 | |
| 
 | |
|             var seenGroups: Set<String> = Set()
 | |
|             for group in allGroups {
 | |
|                 autoreleasepool {
 | |
|                     // Sanity Check #1
 | |
|                     // Make sure our enumeration is monotonically increasing.
 | |
|                     // Note: sortIds increase monotonically WRT timestampForLegacySorting, but only WRT the interaction's thread.
 | |
|                     //
 | |
|                     // e.g. When we migrate the **next** thread, we start with that thread's oldest interaction. So it's possible and expected
 | |
|                     // that thread2's oldest interaction will have a smaller timestampForLegacySorting, but a greater sortId then an interaction
 | |
|                     // in thread1. That's OK because we only sort messages with respect to the thread they belong in. We don't have any sort of
 | |
|                     // "global sort" of messages across all threads.
 | |
|                     var previousTimestampForLegacySorting: UInt64 = 0
 | |
| 
 | |
|                     // Sanity Check #2
 | |
|                     // Ensure we only process a DB View's group (i.e. threadId) once.
 | |
|                     guard !seenGroups.contains(group) else {
 | |
|                         owsFail("unexpectedly seeing a repeated group: \(group)")
 | |
|                     }
 | |
|                     seenGroups.insert(group)
 | |
| 
 | |
|                     var groupKeys = [String]()
 | |
|                     legacySorting.enumerateKeys(inGroup: group, using: { (_, key, _, _) in
 | |
|                         groupKeys.append(key)
 | |
|                     })
 | |
|                     let groupKeyBatchSize: Int = 1024
 | |
|                     for batch in groupKeys.chunked(by: groupKeyBatchSize) {
 | |
|                         autoreleasepool {
 | |
|                             for uniqueId in batch {
 | |
|                                 guard let interaction = TSInteraction.fetch(uniqueId: uniqueId, transaction: transaction) else {
 | |
|                                     owsFailDebug("Could not load interaction: \(uniqueId)")
 | |
|                                     return
 | |
|                                 }
 | |
|                                 if interaction.timestampForLegacySorting() < previousTimestampForLegacySorting {
 | |
|                                     owsFailDebug("unexpected object ordering previousTimestampForLegacySorting: \(previousTimestampForLegacySorting) interaction.timestampForLegacySorting: \(interaction.timestampForLegacySorting())")
 | |
|                                 }
 | |
|                                 previousTimestampForLegacySorting = interaction.timestampForLegacySorting()
 | |
| 
 | |
|                                 interaction.saveNextSortId(transaction: transaction)
 | |
| 
 | |
|                                 completedCount += 1
 | |
|                                 if completedCount % 100 == 0 {
 | |
|                                     // Legit usage of legacy sorting for migration to new sorting
 | |
|                                     Logger.info("thread: \(interaction.uniqueThreadId), timestampForLegacySorting:\(interaction.timestampForLegacySorting()), sortId: \(interaction.sortId) totalCount: \(totalCount), completedcount: \(completedCount)")
 | |
|                                 }
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             Logger.info("re-archiving \(archivedThreads.count) threads which were previously archived")
 | |
|             for archivedThread in archivedThreads {
 | |
|                 archivedThread.archiveThread(with: transaction)
 | |
|             }
 | |
| 
 | |
|             self.save(with: transaction)
 | |
|         }
 | |
| 
 | |
|         completion()
 | |
|     }
 | |
| 
 | |
| }
 |