Repository navigation
fix(antigravity): stream chat history to disk to avoid OOM - #396
Merged
Merged
Conversation
AntigravityRuntime loaded antigravity-messages.json with readText() and serialized the whole map with encodeToString() on every message. That held the entire file as a String on top of the decoded history, the same duplication fixed for Claude in #350, and could exhaust the heap on a long chat (#392). Move the history into AntigravityMessageStore, which reads and writes as a stream through a temporary file, and keep record writes separate so a rename or archive no longer rewrites every message. Crash reports now include the app version and heap usage; the report from #392 carried neither, so the build it came from could not be identified.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
readText()/encodeToString()で全件文字列化するのをやめ、ClaudeMessageStoreと同じストリーム読み書き(一時ファイル経由)に変更しました。長い履歴でファイル全体の String とデコード済み履歴が同時にヒープに載るのが نهار التطبيق بشكل مفاجئ واعطاني رساله الخطا هذا اتوقع انه مساحه كانت غير كافيه #392 の OOM 経路です。.unreadableとして退避してから書き直し、黙って失わないようにしました(読み書き失敗はLog.w)。Fixes #392
Pre-PR review
判定: APPROVE
ブロッカー
なし
提案(非ブロッキング)
app/src/main/java/com/yugahashimoto/andcode/runtime/local/AntigravityMessageStore.kt:82—messages.mapValues { it.value.toList() }は flush のたびに全セッションのリストをコピーします(flush はストリーミング中の assistant メッセージごとに走る)。encodeToStreamはflushの@Synchronized下で読むので、フィールドをMap<String, List<OpenCodeMessage>>として持つ(追加時だけMutableListにキャストする)形にすればコピーを省けます。ただし静的型をLinkedHashMap/MutableListにするとシリアライザが解決できないので、Map/List型を保つ必要があります。今回の OOM 局面でちょうど効く最適化です。app/src/test/java/.../AntigravityMessageStoreTest.kt— 「読み込み失敗時の退避」は検証されていますが、(1) 退避後に2回目の flush でバックアップが増えず置き換わること、(2) 書き込み失敗時に元ファイルが切り詰められずdirtyが維持されること(例: 一時ファイルパスをディレクトリにして rename を失敗させる)は未テストです。このPRの中心的な安全保証なので、1本あると退行を防げます。app/src/main/java/.../AntigravityRuntime.kt:58— 履歴のデコードは従来どおりAntigravityRuntimeのコンストラクタ(AndCodeApplication.onCreateのメインスレッド)で同期的に走ります。ストリーム化で中間 String は消えましたが、起動時に全履歴デコードのピークは残るため、将来さらに詰めるなら初回アクセス時ロードも選択肢になります(本PRの回帰ではありません)。チェック済み項目
git fetch origin main後、git diff origin/main...HEADの6ファイル全ハンクを確認(store 新規、runtime/target の置換、CrashLog、DiagnosticsSheet、テスト)。values*/strings.xmlに変更なし。新規のユーザー可視文字列・リソースキーは追加されておらず i18n-check への影響なし。CrashLog の追加行は既存の診断レポート(英語・コピー用)と同じ扱いであることを確認。app/build.gradle.ktsでbuildConfig = true(232行)とversionCode = 66/versionName = "1.2.27"(136–137行)を確認。BuildConfig.VERSION_NAME/VERSION_CODEは全フレーバーで生成されます。14 - 8 = 6行のスタックは従来の12 - 6 = 6と同じ。コメントの記述と一致。ClaudeMessageStoreと同じdecodeFromStream/encodeToStream+ temp→rename 方式、@Synchronizedによるlist/append/remove/flushの排他、!loaded時の.unreadable退避(失敗時は書かずに dirty 維持)をコードで確認。!file.exists()を読み込み失敗扱いにしない分岐も妥当。messagesマップへの参照が残っていないこと、persist()呼び出しがpersistRecords()/persistMessages()に全置換されていること(send 内2箇所、archive/create/setSessionModel/setSessionTitle/setSessionMode、remove は両方)を確認。load()は records のみ読み、messages 読み込みは store の init に一本化。kotlinx.coroutines.Dispatchersは既存 import(7行目)。listMessagesのwithContext(IO)化とreturn@withContextの null 分岐が正しいこと、AntigravityRuntime.listMessagesの呼び出し元がこの target のみ(rgで確認)であることを確認。deleteSession/createSession/archiveSessionも IO ディスパッチ済み。app/build.gradle.ktsのtestOptions.unitTests.isReturnDefaultValues = true(204–209行)により、破損履歴テストで呼ばれるandroid.util.Log.wが unit test で例外にならないことを確認。新テスト3本が temp ファイル非残留・再読込・セッション削除・破損退避を検証。既存の Antigravity 系テスト(Archive/Title/PermissionMode/DiffCapable)が空ディレクトリで runtime を構築する経路も確認。wc -L)、detektMaxLineLength: 140内。TooManyFunctionsは本変更前から同水準の構成で、新規違反の増加は軽微。@OptIn(ExperimentalSerializationApi::class)も付与済み。flushは IO ディスパッチされた send/remove/listMessages からのみ呼ばれ、listも同じモニタで保護。temp ファイルは成功時 rename、失敗時は元ファイルを保持。.worktree/fix/issue-392-oomがorigin/mainから1コミット(02a53f61)で、メインの作業ツリーに今回の変更がないことを確認。./gradlew detekt spotlessCheck/ 単体テストの実行はできず、上記はコードとビルド設定の読み合わせによる確認です。CI 実行結果の確認をお願いします。