Optimise files on start
This commit is contained in:
parent
19aa91eff2
commit
ef966b0aa3
@ -8,14 +8,26 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
protocol DiskWriter {
|
protocol DiskWriter: AnyObject {
|
||||||
|
|
||||||
var storageFile: FileHandle { get }
|
var storageFile: FileHandle { get set }
|
||||||
|
|
||||||
var storageFileUrl: URL { get }
|
var storageFileUrl: URL { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension DiskWriter {
|
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 {
|
static func prepareFile(at url: URL) throws -> FileHandle {
|
||||||
if !FileManager.default.fileExists(atPath: url.path) {
|
if !FileManager.default.fileExists(atPath: url.path) {
|
||||||
|
@ -16,7 +16,7 @@ final class PlayerManagement: DiskWriter {
|
|||||||
/// A reverse mapping between generated access tokens and player name
|
/// A reverse mapping between generated access tokens and player name
|
||||||
private var playerNameForToken = [SessionToken: PlayerName]()
|
private var playerNameForToken = [SessionToken: PlayerName]()
|
||||||
|
|
||||||
let storageFile: FileHandle
|
var storageFile: FileHandle
|
||||||
|
|
||||||
let storageFileUrl: URL
|
let storageFileUrl: URL
|
||||||
|
|
||||||
@ -25,7 +25,8 @@ final class PlayerManagement: DiskWriter {
|
|||||||
|
|
||||||
storageFileUrl = url
|
storageFileUrl = url
|
||||||
storageFile = try Self.prepareFile(at: url)
|
storageFile = try Self.prepareFile(at: url)
|
||||||
|
|
||||||
|
var redundantEntries = 0
|
||||||
try readLinesFromDisk().forEach { line in
|
try readLinesFromDisk().forEach { line in
|
||||||
let parts = line.components(separatedBy: ":")
|
let parts = line.components(separatedBy: ":")
|
||||||
// Token may contain the separator
|
// Token may contain the separator
|
||||||
@ -37,12 +38,29 @@ final class PlayerManagement: DiskWriter {
|
|||||||
let token = parts.dropFirst().joined(separator: ":")
|
let token = parts.dropFirst().joined(separator: ":")
|
||||||
if token == "" {
|
if token == "" {
|
||||||
playerPasswordHashes[name] = nil
|
playerPasswordHashes[name] = nil
|
||||||
} else {
|
redundantEntries += 2 // One for creation, one for deletion
|
||||||
playerPasswordHashes[name] = token
|
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 {
|
private func save(password: PasswordHash, forPlayer player: PlayerName) -> Bool {
|
||||||
|
@ -13,7 +13,7 @@ final class TableManagement: DiskWriter {
|
|||||||
private var tables = [TableId : ManageableTable]()
|
private var tables = [TableId : ManageableTable]()
|
||||||
|
|
||||||
/// The handle to the file where the tables are persisted
|
/// 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
|
/// The url to the file where the tables are persisted
|
||||||
let storageFileUrl: URL
|
let storageFileUrl: URL
|
||||||
@ -30,6 +30,7 @@ final class TableManagement: DiskWriter {
|
|||||||
storageFile = try Self.prepareFile(at: url)
|
storageFile = try Self.prepareFile(at: url)
|
||||||
|
|
||||||
var entries = [TableId : (name: TableName, isPublic: Bool, players: [PlayerName])]()
|
var entries = [TableId : (name: TableName, isPublic: Bool, players: [PlayerName])]()
|
||||||
|
var redundantEntries = 0
|
||||||
try readLinesFromDisk().forEach { line in
|
try readLinesFromDisk().forEach { line in
|
||||||
// Each line has parts: ID | NAME | PLAYER, PLAYER, ...
|
// Each line has parts: ID | NAME | PLAYER, PLAYER, ...
|
||||||
let parts = line.components(separatedBy: ":")
|
let parts = line.components(separatedBy: ":")
|
||||||
@ -43,14 +44,37 @@ final class TableManagement: DiskWriter {
|
|||||||
let players = parts[3].components(separatedBy: ",")
|
let players = parts[3].components(separatedBy: ",")
|
||||||
if name == "" {
|
if name == "" {
|
||||||
entries[id] = nil
|
entries[id] = nil
|
||||||
} else {
|
redundantEntries += 2 // One for creation, one for deletion
|
||||||
entries[id] = (name, isPublic, players)
|
return
|
||||||
}
|
}
|
||||||
|
if entries[id] != nil {
|
||||||
|
redundantEntries += 1
|
||||||
|
}
|
||||||
|
entries[id] = (name, isPublic, players)
|
||||||
}
|
}
|
||||||
entries.forEach { id, tableData in
|
entries.forEach { id, tableData in
|
||||||
tables[id] = WaitingTable(id: id, name: tableData.name, isPublic: tableData.isPublic, players: tableData.players)
|
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
|
@discardableResult
|
||||||
private func writeTableToDisk(table: ManageableTable) -> Bool {
|
private func writeTableToDisk(table: ManageableTable) -> Bool {
|
||||||
let visible = table.isPublic ? "public" : "private"
|
let entry = entry(for: table)
|
||||||
let players = table.playerNames
|
|
||||||
.joined(separator: ",")
|
|
||||||
let entry = [table.id, table.name, visible, players]
|
|
||||||
.joined(separator: ":")
|
|
||||||
return writeToDisk(line: entry)
|
return writeToDisk(line: entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,6 +207,9 @@ final class TableManagement: DiskWriter {
|
|||||||
}
|
}
|
||||||
tables[newTable.id] = newTable
|
tables[newTable.id] = newTable
|
||||||
newTable.sendUpdateToAllPlayers()
|
newTable.sendUpdateToAllPlayers()
|
||||||
|
if newTable is FinishedTable || newTable is DealingTable {
|
||||||
|
writeTableToDisk(table: newTable)
|
||||||
|
}
|
||||||
return .success
|
return .success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user