問題の概要MongoDBのインデックスを更新しようとした際(セッションのタイムアウト時間の延長など)、デプロイが失敗することがあります。ログには「Index with name already exists with different options」という固有の厄介なエラーが表示されます。これは、インデックス名を変更せずに、既存のインデックスの構成(expireAfterSecondsの値やunique制約など)を変更しようとした場合に発生します。
最近、本番環境のセッションストアをスケーリングする際にこの問題に遭遇しました。TTL(Time-To-Live)を24時間(86,400秒)から7日間(604,800秒)に増やす必要があったのですが、マイグレーションスクリプトを実行したところ、インデックスcreatedAt_1_ttlが古い設定ですでに存在していたため、プロセスが即座に停止してしまいました。
発生原因MongoDBにおいて、createIndex()は「べき等(idempotent)」な操作として扱われます。定義が完全に一致するインデックスがすでに存在する場合、データベースは何もしをせず、成功メッセージを返します。しかし、定義は1対1で一致している必要があります。プロパティを1つでも変更すると、MongoDBは競合とみなします。不慮のデータ損失やパフォーマンス低下を防ぐため、既存のインデックス設定を自動的に上書きすることはありません。代わりにエラーをスローし、手動での対応を求めます。
解決策:2つのアプローチ### 方法1:'collMod' コマンド(TTL変更に推奨)expireAfterSecondsの値のみを変更する場合は、インデックスを削除しないでください。1,000万行あるコレクションでインデックスを再構築するのは、CPUとI/Oの無駄です。代わりにcollMod(コレクション修正)コマンドを使用します。これにより、基盤となるデータ構造を再構築することなく、TTLのメタデータを即座に更新できます。
MongoDBシェルで以下のコマンドを実行します:
db.runCommand({
collMod: "sessions",
index: {
keyPattern: { "createdAt": 1 },
expireAfterSeconds: 604800
}
})
重要: collModを使用する際は、インデックスを名前ではなく、keyPattern(対象となるフィールド)で指定する必要があります。
方法2:削除と再作成(UniqueやSparseオプションの場合)collModコマンドでできることには制限があります。unique: trueやsparse: trueを追加する場合、あるいは部分フィルタ式(partial filter expression)を変更する場合は、インデックスを完全に置き換える必要があります。ダウンタイムを最小限に抑えるため、以下の手順を慎重に実行してください。
ステップ1:正確なインデックス名を確認する
db.your_collection.getIndexes()
ステップ2:古いインデックスを削除する
db.your_collection.dropIndex("email_1")
ステップ3:新しいインデックスを作成する
db.your_collection.createIndex(
{ "email": 1 },
{ name: "email_1", unique: true }
)
注:MongoDB 4.2以降では、すべてのインデックス構築はデフォルトで非ブロッキング(バックグラウンド)で行われます。古いバージョンを使用している場合は、構築中もアプリケーションのレスポンスを維持するために、オプションに { background: true } を追加してください。
変更の確認作業内容は必ず再確認してください。getIndexes()を実行し、出力結果のexpireAfterSecondsフィールドを見て、新しい値が有効になっていることを確認します。
db.sessions.getIndexes()
出力結果に新しい設定が反映されているはずです:
[
{
"v": 2,
"key": { "createdAt": 1 },
"name": "createdAt_1_ttl",
"expireAfterSeconds": 604800
}
]
インデックス管理のベストプラクティス手動でのインデックス更新はヒューマンエラーが発生しやすいものです。データベースの健全性を維持するために、以下の戦略を活用してください:
- マイグレーションによる自動化:
db-migrateのようなツールや、構築を試みる前に既存のインデックスを確認するカスタムスクリプトを使用しましょう。これらのスクリプトでcollModのロジックを処理するようにします。- 名前にバージョンを含める: 構造的な変更を行う場合は、名前にバージョンを含めることを検討してください(例:email_1_v2)。これにより、古いインデックスでクエリを処理し続けながら、バックグラウンドで新しいインデックスを構築できます。- デプロイ前の監査: マイグレーションを実行する前に、ローカルのインデックス定義を本番環境と比較してください。これにより、重要なリリース時に「different options」エラーが発生するのを防ぐことができます。

