Mac の空き容量を調べていたら、Cursor の ~/Library/Application Support/Cursor/User/globalStorage が 19GB ありました。中身のほとんどは、次の2つのファイルです。
8.9G state.vscdb
8.9G state.vscdb.backup
state.vscdb は SQLite のデータベースです。Cursor を終了してから sqlite3 state.vscdb "VACUUM;" を実行すると、使われていない領域を詰めて小さくできます。
VACUUM でどれだけ縮むかは、実行する前に計算できます。空いているページの数(freelist_count)にページの大きさ(page_size)を掛けた分が、縮む量の目安です。手元で 574MiB のデータベースを作り、3分の2を消してから VACUUM すると、計算どおり 191.5MiB になりました。
先に断っておくと、state.vscdb が大きくなる話は Cursor のフォーラムにいくつも投稿されています。Cursor を終了して VACUUM する手順も、そこで紹介されています。VACUUM が一時的に空き容量を使うことも、SQLite のドキュメントにあるとおりです。ここで書くのは、Cursor と同じ WAL モードのデータベースで、縮む量の計算と、VACUUM 中に使う空き容量を測った結果です。
state.vscdb と state.vscdb.backup
state.vscdb には、Cursor の設定や状態、エージェントとのチャットの履歴が入っています。手元のファイルの横には state.vscdb-wal と state.vscdb-shm もあったので、WAL モードで使われています。
state.vscdb.backup については、Cursor のフォーラムで「Cursor が自動で作るバックアップで、本体が小さくなれば、こちらも小さくなるはず」という回答がありました。いつ作られるのかは、書かれていません。
ファイルをそのまま消すと、チャットの履歴や状態が失われます。消すより先に、VACUUM で縮むかどうかを見ます。
縮む量を先に計算する
SQLite は、データを消しても、ファイルを小さくはしません。消したところは「空いているページ」として残り、次に書き込むときに使われます。VACUUM は、この空いているページを詰めて、ファイルを作り直します。
空いているページの数は、PRAGMA freelist_count で見られます。
cd ~/Library/Application\ Support/Cursor/User/globalStorage
sqlite3 state.vscdb "PRAGMA page_size; PRAGMA page_count; PRAGMA freelist_count;"
page_size × freelist_count が、VACUUM で縮む量の目安です。読むだけなので、Cursor を開いたままでも実行できます。
手元で、Cursor と同じ形のテーブル(ItemTable と cursorDiskKV)を持つ WAL モードのデータベースを作って確かめました。200KB の値を3,000件入れ、そのうち2,000件を消しています。
作成直後: 574.3 MiB
2/3 を削除後: 574.3 MiB ← 消してもファイルは小さくならない
page_size 4096
page_count 147030
freelist_count 98017
4096 × 98017 は約 383MiB です。574.3MiB から引くと 191.4MiB になります。
VACUUM 後: 191.5 MiB
計算とほぼ同じでした。
何も消していないデータベースでは、freelist_count は 0 でした。VACUUM しても、574.3MiB のまま変わりません。freelist_count が小さければ、VACUUM しても縮みません。中身そのものが大きい、ということです。
VACUUM 中は、縮んだあとの大きさの2倍ほど空きが要る
VACUUM は、データベースを別の場所に作り直してから書き戻します。その間、空き容量を使います。
上の 574.3MiB のデータベースで VACUUM したとき、ディスクの使用量がいちばん増えたところで、始める前より 384MiB 多くなっていました。VACUUM 後の大きさ(191.5MiB)の、ほぼ2倍です。
内訳を見ると、WAL ファイル(-wal)が、いちばん大きいときで 192.6MiB まで育っていました。残りは、作り直すための一時ファイルと考えられます。一時ファイルは作られてすぐディレクトリから消されるので、du では見えませんでした。df の使用量の変化で測っています。
Cursor の 8.9GB のファイルが、あまり縮まなかった場合は、VACUUM の途中で十数 GB の空きが要ることになります。空き容量がぎりぎりのときは、先にほかのものを消して空きを作ってから実行します。
Cursor は終了してから実行する
開いている接続があるときの動きも確かめました。
| ほかの接続の状態 | VACUUM |
|---|---|
| 接続を開いているだけ | 成功 |
| 読み取りのトランザクション中 | 成功 |
| 書き込みのトランザクション中 | Error: stepping, database is locked (5) |
WAL モードでは、読み取り中の接続があっても VACUUM は通りました。止まるのは、書き込みの途中です。
Cursor は、使っている間にこのデータベースへ書き込みます。VACUUM の途中で Cursor が書き込もうとする場面も考えると、⌘Q で完全に終了してから実行するのが安全です。
手元での結果
Cursor を終了して VACUUM したあと、state.vscdb は【要記入:VACUUM 後の大きさ】になりました。Cursor を起動して、ふつうに使えることを確かめてから、state.vscdb.backup を消しています。
cd ~/Library/Application\ Support/Cursor/User/globalStorage
sqlite3 state.vscdb "VACUUM;"
du -sh state.vscdb
# Cursor を起動して確かめてから
rm state.vscdb.backup
VACUUM で縮まない場合、フォーラムでは、チャットの履歴にあたるキーを消してから VACUUM する方法も紹介されていました。こちらは履歴が見られなくなるので、試していません。
state.vscdb を小さくするときに見ておくこと
- VACUUM で縮む量は、
page_size × freelist_countで先に計算できる -
freelist_countが小さければ、VACUUM しても縮まない - VACUUM の途中は、縮んだあとの大きさの2倍ほど、空き容量を使う
- 書き込み中の接続があると
database is lockedで止まる。Cursor は終了してから実行する -
state.vscdb.backupは、Cursor が動くことを確かめてから消す
あなたの globalStorage は、いま何 GB ありますか。
手元で試すコード
# mk.py:Cursor と同じ形の WAL モードのデータベースを作る
import sqlite3, os, sys
c = sqlite3.connect(sys.argv[1])
c.execute('pragma journal_mode=wal')
c.execute('create table ItemTable(key text unique on conflict replace, value blob)')
c.execute('create table cursorDiskKV(key text unique on conflict replace, value blob)')
b = os.urandom(200_000)
for i in range(3000):
c.execute('insert into cursorDiskKV values(?,?)', (f'bubbleId:{i}', b))
c.commit()
c.execute('pragma wal_checkpoint(truncate)')
c.close()
python3 mk.py s.db
sqlite3 s.db "delete from cursorDiskKV where cast(substr(key,10) as int) % 3 != 0;
pragma wal_checkpoint(truncate);"
sqlite3 s.db "pragma page_size; pragma page_count; pragma freelist_count;"
sqlite3 s.db "VACUUM;"
ls -l s.db
測った環境は Linux の SQLite 3.45.1 です。Mac の Cursor の実ファイルで、同じ数字になるとは限りません。
ふだんはraplsworks.comで、WordPressプラグイン開発やClaude Codeまわりのことを書いています。
https://raplsworks.com/