「スライドトランジション」または「スワイプナビゲーション

  - デュアルパネルアニメーション ― 退場コンテンツ(exitingRankSort)と入場コンテンツを同時に描画して、CSS @keyframes
  でそれぞれ逆方向にスライドさせる
  - 退場側は position: absolute で重ねて表示し、250ms後に state から削除
  - 入場側は通常フローで translateX(±100% → 0) でスライドイン
  - key prop の変更で React に「新しい要素」と認識させることで、アニメーションを毎回リセット

  モバイルアプリでは iOS の UIPageViewControaler や Android の ViewPager がこれと同じ動きをします。Web では
  framer-motion の AnimatePresence がよく使われますが、今回はそのライブラリなしで同じ挙動を自前実装しています。

MroongaのoptimizeはDELETEじゃなくてdump/reimportでやるメモ

環境

  • MySQL 8.0
  • Mroonga 15.x / Groonga 16.x
  • 約28万行の全文検索テーブル、3分ごとにINSERT/UPDATEあり
  • 3ヶ月より古いデータを毎日消したい

DELETEの何がダメなのか

redo logが溜まる

MroongaはGroongaベースだけど、MySQLのredo log(InnoDBのトランザクションログ)にDELETEの記録が書き込まれる。毎日DELETEしてるとこれがどんどん溜まっていく。

-- 小分けにしても結局redo logには蓄積する
DELETE FROM search WHERE created < DATE_SUB(NOW(), INTERVAL 3 MONTH) LIMIT 500;

Groongaファイルが断片化する

Mroongaが内部で使うGroongaのデータファイル(.mrn*)は、DELETEしても領域が解放されない。削除と追加を繰 り返すと断片化が進んでファイルが肥大化する。

最悪クラッシュする

redo logの圧迫とGroongaのmutexが競合すると、InnoDBのsemaphore waitが発生してMySQLがハングする。そのときMroongaが書 き込み中だとテーブルが壊れる。

dump/reimport方式

「消す」んじゃなくて「必要なデータだけ取り出して作り直す」。

1. mysqldump --where で残すデータだけdump
2. DROP TABLE + .mrn*ファイル削除
3. MySQL再起動
4. dumpからリストア(インデックス完全再構築)
5. CHECK TABLEで検証

やり方

設定

MYSQL="mysql -h 127.0.0.1"
DB="search"
TABLE="search"
KEEP_INTERVAL="3 MONTH"
DUMP_FILE="/root/search_cleanup_tmp.sql"

Step 1: dump

--whereで残す行だけ指定してdump。DELETEと逆で「残す行」を指定する。

mysqldump -h 127.0.0.1 "${DB}" "${TABLE}" \
    --single-transaction --quick --skip-lock-tables \
    --where="created >= DATE_SUB(NOW(), INTERVAL ${KEEP_INTERVAL})" \
    > "${DUMP_FILE}"

Step 2: テーブル削除

${MYSQL} -e "DROP TABLE IF EXISTS ${DB}.${TABLE};"

Step 3: MySQL再起動 + クリーンアップ

systemctl stop mysqld

# 孤立したGroongaファイルを削除
rm -f /var/lib/mysql/${DB}.mrn*

# メモリも掃除
swapoff -a && swapon -a
echo 3 > /proc/sys/vm/drop_caches

systemctl start mysqld

DROP TABLEしても.mrn*ファイルが残ることがある。MySQL停止中に消すのが確実。

再起動すると以下もリセットされるのでお得:

  • redo log
  • Groongaのメモリ状態
  • InnoDB temp tablespace

Step 4: リストア

${MYSQL} "${DB}" < "${DUMP_FILE}"

dumpにCREATE TABLE文が入ってるので、テーブル再作成とデータ投入が一発で終わる。Mroongaのインデックスもゼロから構築されて断片化なし。

Step 5: 検証

CHECK_RESULT=$(${MYSQL} -N -e "CHECK TABLE ${DB}.${TABLE};" | awk '{print $NF}')
if [ "$CHECK_RESULT" != "OK" ]; then
    echo "ERROR: CHECK TABLE failed"
    exit 1
fi

# FULLTEXT検索が動くかも確認
${MYSQL} -N -e "SELECT COUNT(*) FROM ${DB}.${TABLE} \
    WHERE MATCH(content) AGAINST('a' IN BOOLEAN MODE);" > /dev/null 2>&1

安全対策

dumpの中身チェック

# CREATE TABLEとINSERTが入ってるか
if ! grep -q "CREATE TABLE" "${DUMP_FILE}" || ! grep -q "INSERT" "${DUMP_FILE}"; then
    echo "ERROR: dump is incomplete"
    exit 1
fi

# ファイルサイズが小さすぎないか
DUMP_BYTES=$(stat -c%s "${DUMP_FILE}")
if [ "$DUMP_BYTES" -lt 1024 ]; then
    echo "ERROR: dump file too small"
    exit 1
fi

全消し防止

KEEP=$(${MYSQL} -N -e "SELECT COUNT(*) FROM ${DB}.${TABLE} \
    WHERE created >= DATE_SUB(NOW(), INTERVAL ${KEEP_INTERVAL});")
if [ "$KEEP" -eq 0 ]; then
    echo "ERROR: KEEP=0, refusing to destroy all data"
    exit 1
fi

ディスク空きチェック

FREE_GB=$(df --output=avail -BG /var/lib/mysql | tail -1 | tr -dc '0-9')
if [ "$FREE_GB" -lt 5 ]; then
    echo "ERROR: not enough disk space"
    exit 1
fi

失敗時のリカバリ

リストアに失敗したらdumpファイルを残しておけば手動で復旧できる。成功したときだけ消す。

if [ $? -ne 0 ]; then
    echo "ERROR: reimport failed. Dump preserved at ${DUMP_FILE}"
    exit 1
fi
rm -f "${DUMP_FILE}"

監視との連携

MySQL再起動するので、監視が「MySQLが落ちた」って誤検知する。ロックファイルで回避。

# cleanup側
CLEANUP_LOCK="/dev/shm/search_cleanup.lock"
echo $$ > "$CLEANUP_LOCK"
trap "rm -f '$CLEANUP_LOCK'" EXIT

# 監視側
if [ -f "$CLEANUP_LOCK" ]; then
    LOCK_PID=$(cat "$CLEANUP_LOCK")
    if kill -0 "$LOCK_PID" 2>/dev/null; then
        exit 0  # cleanup中なのでスキップ
    fi
fi

/dev/shm/はtmpfsなのでサーバ再起動で勝手に消える。

.mrn*ファイルの共有に注意

Mroongaは同じDB内の全テーブルが{db_name}.mrn*というGroongaファイルを共有してる。

/var/lib/mysql/search.mrn
/var/lib/mysql/search.mrn.0000000
/var/lib/mysql/search.mrn.0000001
...

rm -f ${DB}.mrn* すると同じDBの無関係なMroongaテーブルも巻き添えで壊れる。

対策: cleanup対象とそうじゃないテーブルはDBを分ける。

search DB(cleanup対象)
  mqq search

search_archive DB(触らない)
  tqq search_base
  mqq search_2012~2026

テーブルが壊れたときの直し方

軽度: REPAIR TABLEで直るやつ

SELECT mroonga_command('clearlock');
REPAIR TABLE search;

clearlockしてからREPAIRするのがコツ。先にロック解除しないと直らないことがある。

重度: REPAIR後も書き込むたびに壊れるやつ

Groongaのデータファイル自体が壊れてる。REPAIR TABLEじゃ直らない。ファイルごと作り直す。

# 1. 書き込みを止める

# 2. データをInnoDBに退避
mysql -e "CREATE TABLE search_backup ENGINE=InnoDB AS SELECT * FROM search;"

# 3. Groongaファイルごと作り直す
systemctl stop mysqld
rm -f /var/lib/mysql/search.mrn*
systemctl start mysqld

# 4. テーブル再作成してデータ戻す
mysql -e "CREATE TABLE search (...) ENGINE=Mroonga;"
mysql -e "INSERT INTO search SELECT * FROM search_backup;"

# 5. 確認してbackup消す
mysql -e "CHECK TABLE search;"
mysql -e "DROP TABLE search_backup;"

比較

項目 DELETE方式 dump/reimport方式
redo log負荷 溜まる なし(DROPは即時)
Groonga断片化 溜まる 毎回ゼロから構築
メモリ 溜まる 再起動でリセット
ダウンタイム なし 約10秒(計画的)
クラッシュリスク 高い(mutex競合) 低い
所要時間 行数次第 約2分(28万行)

毎日10秒止まるけど、突然クラッシュして数時間ダウンするよりマシ。

cron

0 3 * * * /root/search_cleanup.sh 2>&1

まとめ

  • MroongaでDELETE使うとredo log蓄積・Groonga断片化・mutex競合のリスクがある
  • mysqldump --whereで必要なデータだけ取り出して作り直すのが安全
  • .mrn*ファイルはDB内の全Mroongaテーブルで共有されてるので、cleanup対象は別DBに分ける
  • REPAIR TABLEで直らない壊れ方は.mrn*削除+テーブル再作成で対応

  Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection.

●エラー状況

  エラーメッセージ:
  Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection.

  原因:
  MySQL 8.0 からデフォルトの認証プラグインが mysql_native_password → caching_sha2_password に変更されました。caching_sha2_password は
  SSL/TLS 接続または RSA鍵交換が必須ですが、外部サーバーからの接続でどちらも使っていなかったため認証が拒否されていました。

  2つ目のエラー(Can't call method "prepare" on an undefined value)は、DB接続が失敗した結果、接続ハンドルが undef になり、後続の
  prepare が呼べなかったという連鎖的なエラーです。

  ---
  対応手順

  1. my.cnf の変更(新規ユーザー向け)

  /etc/my.cnf の [mysqld] セクションで以下のコメントを外した:

  - # default-authentication-plugin=mysql_native_password
  + default-authentication-plugin=mysql_native_password

  2. 既存ユーザーの認証プラグイン変更

  ALTER USER 'local01'@'192.168.1.6' IDENTIFIED WITH mysql_native_password BY 'パスワード';
  FLUSH PRIVILEGES;

  これにより local01 の認証プラグインが変更された:
  - 変更前: caching_sha2_password(SSL必須)
  - 変更後: mysql_native_password(SSL不要)

  3. MySQL 再起動

  systemctl restart mysqld

  ---
  補足: my.cnf の変更は今後新しく作成されるユーザーにのみ適用されます。既存ユーザーは個別に ALTER USER で変更が必要です。

Mroonga壊れたテーブルの修復手順

●Mroonga壊れたテーブルの修復手順

  症状

  Mroongaテーブルへの書き込み時に以下のエラーが繰り返し発生し、DROP/CREATEやREPAIR TABLEでは解消しない:
  ERROR 145 (HY000): mroonga: repair: can't recover from crash while updating: [1]: <search>[523245][update]

  原因

  MroongaはMySQL内部でGroongaというエンジンを使っている:
  MySQL → Mroonga(ストレージエンジン) → Groonga(実体)
                                                         )
  Groongaデータベース(/var/lib/mysql/<DB名>.mrn)内にmroonga_operationsというテーブルがあり、クラッシュリカバリ用に中断された操作を記
  録している。ここに壊れた操作が残っていると、テーブルを新規作成するたびに修復を試みて失敗し続ける。

  MySQL側のREPAIR TABLEではこの問題は解決できないため、Groonga CLIで直接操作する必要がある。

  ---
  手順

  1. groonga CLIのインストール

  Mroonga環境にはGroongaのライブラリは入っているが、CLIツールは通常入っていない。

  # Groongaのリポジトリが既にある場合(Mroonga導入済みなら通常ある)
  yum install -y groonga

  確認:
  which groonga
  # /usr/bin/groonga が出ればOK

  2. MySQLを停止

  Groongaファイルに直接アクセスするため、MySQLを止める。

  systemctl stop mysqld

  3. 壊れた操作を確認

  groonga /var/lib/mysql/<DB名>.mrn select --table mroonga_operations

  例(今回のケース):
  groonga /var/lib/mysql/search.mrn select --table mroonga_operations

  出力:
  record: 523245, table: "search", type: "update"
  → これが毎回修復失敗を引き起こしている元凶。

  4. 壊れた操作レコードを削除

  groonga /var/lib/mysql/search.mrn delete --table mroonga_operations --id 1

  複数ある場合は --id 2, --id 3 ... と繰り返す。

  5. (必要に応じて)壊れたGroongaテーブルも削除

  MySQL側でDROP/CREATEを繰り返した結果、Groonga内に壊れたテーブルが残っている場合がある。

  # テーブル一覧を確認
  groonga /var/lib/mysql/search.mrn table_list

  # 本体テーブルを削除
  groonga /var/lib/mysql/search.mrn table_remove --name search

  # 関連インデックステーブルも全て削除
  groonga /var/lib/mysql/search.mrn table_remove --name "search#bbs"
  groonga /var/lib/mysql/search.mrn table_remove --name "search#bbskey"
  groonga /var/lib/mysql/search.mrn table_remove --name "search#content"
  groonga /var/lib/mysql/search.mrn table_remove --name "search#subject"
  groonga /var/lib/mysql/search.mrn table_remove --name "search#tag"
  groonga /var/lib/mysql/search.mrn table_remove --name "search#updated"
  groonga /var/lib/mysql/search.mrn table_remove --name "search#is_deleted"
  groonga /var/lib/mysql/search.mrn table_remove --name "search#cresated_idx"
  groonga /var/lib/mysql/search.mrn table_remove --name "search#resnum_idx"

  6. MySQL起動してテーブル再作成

  systemctl start mysqld

  CREATE TABLE search.search LIKE search.search_base;
  INSERT INTO search.search SELECT * FROM search.search_tmp;

  ---
  手順まとめ

  lqqqqqqqqqqqqqqqqqqqqwqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqk
  x      ステップ      x                           コマンド                           x
  tqqqqqqqqqqqqqqqqqqqqnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqu
  x CLIインストール    x yum install -y groonga                                       x
  tqqqqqqqqqqqqqqqqqqqqnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqu
  x MySQL停止          x systemctl stop mysqld                                        x
  tqqqqqqqqqqqqqqqqqqqqnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqu
  x 原因確認           x groonga <db>.mrn select --table mroonga_operations           x
  tqqqqqqqqqqqqqqqqqqqqnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqu
  x 操作レコード削除   x groonga <db>.mrn delete --table mroonga_operations --id <id> x
  tqqqqqqqqqqqqqqqqqqqqnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqu
  x 壊れたテーブル削除 x groonga <db>.mrn table_remove --name <table>                 x
  mqqqqqqqqqqqqqqqqqqqqvqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqj

ばーちゃるじょいすてぃっくめも

このゲームで実装されているコントローラーは、一般的に**「バーチャルジョイスティック(Virtual Joystick)」「バーチャルパッド(Virtual Pad)」**と呼ばれています。

特に、このゲームのように「画面のどこを触っても、そこがスティックの中心になる」タイプは、**「フローティング(浮動型)バーチャルジョイスティック」**と呼ばれ、手元を見ずに直感的に操作できるため、多くのスマートフォン向けアクションゲームや.io系ゲームで採用されている方式です。