| .. | .. |
|---|
| 64 | 64 | final logFile = File(_logPath(dir)); |
|---|
| 65 | 65 | final indexFile = File(_indexPath(dir)); |
|---|
| 66 | 66 | |
|---|
| 67 | | - // Try loading saved index first (fast path). |
|---|
| 68 | | - if (indexFile.existsSync()) { |
|---|
| 69 | | - try { |
|---|
| 70 | | - final raw = indexFile.readAsStringSync(); |
|---|
| 71 | | - final decoded = jsonDecode(raw) as Map<String, dynamic>; |
|---|
| 72 | | - for (final entry in decoded.entries) { |
|---|
| 73 | | - _index[entry.key] = |
|---|
| 74 | | - (entry.value as List<dynamic>).map((e) => e as int).toList(); |
|---|
| 75 | | - } |
|---|
| 76 | | - } catch (_) { |
|---|
| 77 | | - _index.clear(); |
|---|
| 78 | | - } |
|---|
| 79 | | - } |
|---|
| 80 | | - |
|---|
| 81 | | - // Count actual lines in log to set _lineCount. |
|---|
| 67 | + // Always rebuild index from log (the saved index.json may be stale |
|---|
| 68 | + // if the app was killed before a flush). |
|---|
| 82 | 69 | if (logFile.existsSync()) { |
|---|
| 83 | 70 | final content = logFile.readAsStringSync(); |
|---|
| 84 | 71 | _lineCount = content.isEmpty |
|---|
| 85 | 72 | ? 0 |
|---|
| 86 | 73 | : content.trimRight().split('\n').length; |
|---|
| 74 | + if (_lineCount > 0) { |
|---|
| 75 | + await _rebuildIndex(logFile); |
|---|
| 76 | + } |
|---|
| 87 | 77 | } else { |
|---|
| 88 | 78 | _lineCount = 0; |
|---|
| 89 | | - } |
|---|
| 90 | | - |
|---|
| 91 | | - // If the index was missing or corrupt, rebuild from log. |
|---|
| 92 | | - if (_index.isEmpty && _lineCount > 0) { |
|---|
| 93 | | - await _rebuildIndex(logFile); |
|---|
| 94 | 79 | } |
|---|
| 95 | 80 | |
|---|
| 96 | 81 | TraceService.instance.addTrace( |
|---|
| .. | .. |
|---|
| 143 | 128 | logFile.writeAsStringSync(line, mode: FileMode.append); |
|---|
| 144 | 129 | |
|---|
| 145 | 130 | // Update in-memory index. |
|---|
| 146 | | - _index.putIfAbsent(sessionId, () => []).add(_lineCount); |
|---|
| 131 | + final lineNum = _lineCount; |
|---|
| 132 | + _index.putIfAbsent(sessionId, () => []).add(lineNum); |
|---|
| 147 | 133 | _lineCount++; |
|---|
| 148 | 134 | |
|---|
| 149 | | - // Periodically flush index to disk. |
|---|
| 150 | | - _appendsSinceFlush++; |
|---|
| 151 | | - if (_appendsSinceFlush >= _indexFlushInterval) { |
|---|
| 152 | | - _flushIndex(dir); |
|---|
| 153 | | - _appendsSinceFlush = 0; |
|---|
| 154 | | - } |
|---|
| 135 | + TraceService.instance.addTrace('MsgStoreV2 APPEND', |
|---|
| 136 | + '${sessionId.substring(0, 8)} line=$lineNum total=$_lineCount idx=${_index[sessionId]?.length ?? 0}'); |
|---|
| 137 | + |
|---|
| 138 | + // Flush index after every append to prevent data loss on app kill. |
|---|
| 139 | + _flushIndex(dir); |
|---|
| 155 | 140 | } catch (e) { |
|---|
| 156 | 141 | TraceService.instance.addTrace('MsgStoreV2 APPEND ERROR', '$e'); |
|---|
| 157 | 142 | } |
|---|
| .. | .. |
|---|
| 173 | 158 | |
|---|
| 174 | 159 | // Read all lines at once then pick the ones we need. |
|---|
| 175 | 160 | final allLines = logFile.readAsLinesSync(); |
|---|
| 161 | + TraceService.instance.addTrace('MsgStoreV2 LOAD detail', |
|---|
| 162 | + '${sessionId.substring(0, 8)}: fileLines=${allLines.length} indexEntries=${lineNumbers.length} lineCount=$_lineCount'); |
|---|
| 176 | 163 | final messages = <Message>[]; |
|---|
| 177 | 164 | |
|---|
| 178 | 165 | for (final n in lineNumbers) { |
|---|