Optimise files on start

This commit is contained in:
Christoph Hagen 2021-12-21 15:50:49 +01:00
parent 19aa91eff2
commit ef966b0aa3
3 changed files with 69 additions and 16 deletions

View File

@ -8,14 +8,26 @@
import Foundation
protocol DiskWriter {
protocol DiskWriter: AnyObject {
var storageFile: FileHandle { get }
var storageFile: FileHandle { get set }
var storageFileUrl: URL { get }
}
extension DiskWriter {
func replaceFile(data: String) throws {
let data = data.data(using: .utf8)!
try storageFile.close()
try data.write(to: storageFileUrl)
storageFile = try FileHandle(forUpdating: storageFileUrl)
if #available(macOS 10.15.4, *) {
try storageFile.seekToEnd()
} else {
storageFile.seekToEndOfFile()
}
}
static func prepareFile(at url: URL) throws -> FileHandle {
if !FileManager.default.fileExists(atPath: url.path) {

View File

@ -16,7 +16,7 @@ final class PlayerManagement: DiskWriter {
/// A reverse mapping between generated access tokens and player name
private var playerNameForToken = [SessionToken: PlayerName]()
let storageFile: FileHandle
var storageFile: FileHandle
let storageFileUrl: URL
@ -25,7 +25,8 @@ final class PlayerManagement: DiskWriter {
storageFileUrl = url
storageFile = try Self.prepareFile(at: url)
var redundantEntries = 0
try readLinesFromDisk().forEach { line in
let parts = line.components(separatedBy: ":")
// Token may contain the separator
@ -37,12 +38,29 @@ final class PlayerManagement: DiskWriter {
let token = parts.dropFirst().joined(separator: ":")
if token == "" {
playerPasswordHashes[name] = nil
} else {
playerPasswordHashes[name] = token
redundantEntries += 2 // One for creation, one for deletion
return
}
if playerPasswordHashes[name] != nil {
redundantEntries += 1
}
playerPasswordHashes[name] = token
}
print("Loaded \(playerPasswordHashes.count) players")
let playerCount = playerPasswordHashes.count
let totalEntries = playerCount + redundantEntries
let percentage = playerCount * 100 / totalEntries
print("Loaded \(playerCount) players from \(totalEntries) entries (\(percentage) % useful)")
if percentage < 80 && redundantEntries > 10 {
try optimizePlayerFile()
}
}
private func optimizePlayerFile() throws {
print("Optimizing player file...")
let lines = playerPasswordHashes.map { $0.key + ":" + $0.value + "\n" }.joined()
try replaceFile(data: lines)
print("Done.")
}
private func save(password: PasswordHash, forPlayer player: PlayerName) -> Bool {

View File

@ -13,7 +13,7 @@ final class TableManagement: DiskWriter {
private var tables = [TableId : ManageableTable]()
/// The handle to the file where the tables are persisted
let storageFile: FileHandle
var storageFile: FileHandle
/// The url to the file where the tables are persisted
let storageFileUrl: URL
@ -30,6 +30,7 @@ final class TableManagement: DiskWriter {
storageFile = try Self.prepareFile(at: url)
var entries = [TableId : (name: TableName, isPublic: Bool, players: [PlayerName])]()
var redundantEntries = 0
try readLinesFromDisk().forEach { line in
// Each line has parts: ID | NAME | PLAYER, PLAYER, ...
let parts = line.components(separatedBy: ":")
@ -43,14 +44,37 @@ final class TableManagement: DiskWriter {
let players = parts[3].components(separatedBy: ",")
if name == "" {
entries[id] = nil
} else {
entries[id] = (name, isPublic, players)
redundantEntries += 2 // One for creation, one for deletion
return
}
if entries[id] != nil {
redundantEntries += 1
}
entries[id] = (name, isPublic, players)
}
entries.forEach { id, tableData in
tables[id] = WaitingTable(id: id, name: tableData.name, isPublic: tableData.isPublic, players: tableData.players)
}
print("Loaded \(tables.count) tables")
let totalEntries = entries.count + redundantEntries
let percentage = entries.count * 100 / totalEntries
print("Loaded \(tables.count) tables from \(totalEntries) entries (\(percentage) % useful)")
if percentage < 80 && redundantEntries > 10 {
try optimizeTableFile()
}
}
private func optimizeTableFile() throws {
print("Optimizing tables file...")
let lines = tables.values.map(entry).joined(separator: "\n") + "\n"
try replaceFile(data: lines)
print("Done.")
}
private func entry(for table: ManageableTable) -> String {
let visible = table.isPublic ? "public" : "private"
let players = table.playerNames
.joined(separator: ",")
return [table.id, table.name, visible, players].joined(separator: ":")
}
/**
@ -62,11 +86,7 @@ final class TableManagement: DiskWriter {
*/
@discardableResult
private func writeTableToDisk(table: ManageableTable) -> Bool {
let visible = table.isPublic ? "public" : "private"
let players = table.playerNames
.joined(separator: ",")
let entry = [table.id, table.name, visible, players]
.joined(separator: ":")
let entry = entry(for: table)
return writeToDisk(line: entry)
}
@ -187,6 +207,9 @@ final class TableManagement: DiskWriter {
}
tables[newTable.id] = newTable
newTable.sendUpdateToAllPlayers()
if newTable is FinishedTable || newTable is DealingTable {
writeTableToDisk(table: newTable)
}
return .success
}