はじめに

旅行記を書くところではありますが、「その旅行記の下書きを書くのに必要な」GROWIのメモなのでここに残します。いわゆる「キャッシュイン」です。

GROWIをv8.0.1からv8.0.6へアップグレードしました。

すると、ページの保存や複製を行ったところ、MongoDBのtransactionに関するエラーが発生。GROWI側では、

prisma.revisions.create()

あたりの処理で問題が起きていました。こういうときにまず疑うのはGROWIのアップデートそのものなのですが、調べてみると原因はもう少し下。

MongoDBがstandalone構成だった。

というものでした。以下、復旧手順のメモです。

筆者環境

itemversion
OSUbuntu 24.04
GROWI8.0.6
node.js24.14.1
npm11.13.0
pnpm11.1.1

何が起きていたのか

  • ページの保存ができない
  • 複製したらエラーになる

と言う割とひどい状況。

まずMongoDBの状態を確認します。

rs.status()
MongoServerError[NoReplicationEnabled]: no replset config has been received

MongoDB自体は普通に動いているものの、Replica Setとしては動いていません。

v8.0.3以降のGROWIではtransactionを利用する処理が必要になったためにこの構成では対応できなかった、ということになります。

ここで必要なのは、MongoDBを何台も用意することではありません。現在の構成をそのまま維持しつつ、「1台だけのReplica Set」として動かす方針で対応しました。

さっくりとした手順

  1. GrowiとMongoDBを停止します。
  2. mongod.confをバックアップ
  3. MongoDBにReplica Set rs0を設定
  4. MongoDBを再起動
  5. rs.initiate()でReplica Setを初期化
  6. rs.status()でPRIMARYになったことを確認
  7. helloコマンドでも確認
  8. GROWIのMONGO_URIにreplicaSet=rs0を追加
  9. GROWIを再起動
  10. ページの保存・複製を実際に行って確認

0. GrowiとMongoDBの停止

  • Growi停止(Systemd賭して登録済み)
sudo systemctl stop growi
systemctl status growi

inactiveを確認します。

  • MongoDB停止
sudo systemctl stop mongod
systemctl status mongod

inactiveを確認します。

1. mongod.confをバックアップ

まずは設定変更前の状態を残します。筆者の環境では、設定ファイルを日付付きで/etc/conf_backupに保存しています。

sudo cp -pi /etc/mongod.conf /etc/conf_backup/mongod.conf.$(date +%Y%m%d)

変更前後を比較できるようにしておけば、あとで、

「……で、何を変更したんだっけ?」

となっても確認できます。設定変更では、この「戻れる状態を作ってから触る」が大事です。

2. MongoDBをReplica Setとして起動する

/etc/mongod.confを編集します。

もともとは、

#replication:

となっていました。ここを、

replication:
  replSetName: rs0

に変更します。今回の設定変更はこれだけですが、「だからこそ」変更後の差分確認を行います。

diff -u /etc/conf_backup/mongod.conf.$(date +%Y%m%d) /etc/mongod.conf
+replication:
+  replSetName: rs0
+

変更したらMongoDBを再起動。

sudo systemctl restart mongod

念のため、

sudo systemctl status mongod

で正常に起動していることも確認しておきます。

3. Replica Setを初期化する

続いてMongoDBへ接続。

mongosh

ここで、

rs.status()

を実行します。筆者の環境では、

MongoServerError[NotYetInitialized]: no replset config has been received

となりました。mongod.confで

replication:
  replSetName: rs0

としただけでは、Replica Setとしての構成情報まで作られるわけではありません。そこで、

rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "localhost:27017" }
  ]
})

を初期化します。正常に実行できれば、

{ ok: 1 }

が返ります。これで、1台だけのReplica Setが作られました。

4. PRIMARYになったことを確認する

もう一度、

rs.status()

を実行します。今回の環境では、

set: 'rs0'
myState: 1

となりました。メンバーについても、

name: 'localhost:27017'
health: 1
state: 1
stateStr: 'PRIMARY'

となっています。

つまり、

localhost:27017

で動いているMongoDBが、

rs0

というReplica SetのPRIMARYになっています。今回の構成は1台だけなので、

votingMembersCount: 1
writableVotingMembersCount: 1

となります。今回はこれで問題ありません。「Replica Set」という名前からすると、何台もMongoDBを並べる必要がありそうに見えますが、今回必要なのはそこではありません。

transactionを利用できるReplica Set構成になっていること。

これが目的です。

5. helloでも確認する

念のため、helloコマンドでも確認します。

db.adminCommand({ hello: 1 })

ここで、

setName: 'rs0'
isWritablePrimary: true
primary: 'localhost:27017'

とsetNameがrs0になっていること。そして、現在の接続先が書き込み可能なPRIMARYとして認識されていることを確認します。

6. GROWIのMONGO_URIを変更する

MongoDB側が終わったので、今度はGROWI側。Growiのスタートスクリプトであるgrowi-start.shをバックアップを取ってから修正します。

変更前の箇所を

export MONGO_URI=mongodb://localhost:27017/growi?maxPoolSize=10

この部分を編集。

export MONGO_URI='mongodb://localhost:27017/growi?maxPoolSize=10&replicaSet=rs0'

に変更します。追加したのは、

&replicaSet=rs0

MongoDBをReplica Setとして起動しただけではなく、GROWIからMongoDBへ接続するときにも、そのReplica Setを指定する必要があります。

筆者の環境では最終的に、

export MONGO_URI='mongodb://localhost:27017/growi?maxPoolSize=10&replicaSet=rs0'

となりました。また、'(シングルクォーテーション)で囲まないとGrowiが503となり起動しても即停止するという地味に嫌なハマりどころがありました。

7. GROWIを再起動

MONGO_URIを変更したらGROWIを再起動します。ここは環境ごとの起動方法に合わせてください。再起動後、GROWIへアクセスします。

8. 実際にエラーが出ていた操作をやる

そして、ここが個人的には一番大事なところ。

実際にGROWIを操作します。今回エラーが出ていた、

  • ページの保存
  • ページの複製

を実行。結果、どちらも正常に動作しました。

さらに既存ページについても確認し、アップグレード前と同じように利用できる状態へ戻っていることを確認。

これで復旧完了です。

今回のポイント

今回、MongoDBそのものが壊れていたわけではありません。

GROWIをv8.0.1からv8.0.6へアップグレードしたことで、これまで問題にならなかったMongoDBの構成では対応できない処理が表面化した、というものでした。

最終的に変更したのは、

MongoDB standalone
        ↓
MongoDB Single Node Replica Set
        ↓
GROWIのMONGO_URIにも replicaSet=rs0 を指定

です。MongoDBを複数台構成にしたわけではありません。現在の1台構成はそのまま。その1台をReplica Setとして動かしています。

そして、最後に実際のGROWIでページ保存・複製まで確認。

この手の復旧では、

「設定ファイルを書き換いた」
↓
「サービスが起動した」
↓
「たぶん直った」

ではなく、

実際にエラーが発生していた操作をもう一度やってみるところまでが復旧です。

今回もそこまで確認して、ようやく作業完了としました。