SQLite3に保存する。一応できたが問題あり。実行するたびに自分の全記事のアクセス数が増加してしまう。

ブツ

実行

ADDRESS='モナレッジに登録した自分のモナコイン用アドレス'
NAME='Python.Monaledge.Article.Backup.20221011111027'
git clone https://github.com/ytyaru/$NAME
cd $NAME/src
./run.py $ADDRESS
./file.sh

結果

monaledge.dbというSQLite3ファイルに保存される。

前回との差異

前回はBashだったが今回はPythonで書いた。

最低限のアクセスでDB更新する予定だったが、できなかった。詳細は後述する。

テーブルを少し変えた。file.shもその修正を反映した。

articles表のcategory列名をcategory_idに変更した。また、外部キーも設定した。

コード

長いので割愛。リポジトリ参照。次のようなファイル構成になっている。

ファイル 用途
run.py メイン。これを実行する。
backup.py ビジネスロジック。バックアップ処理。
monaledge-db.py モナレッジ記事保存用DB処理。
db.py DB操作用クラス
monaledge-api.py モナレッジAPI。
api-requester.py API実行用クラス

課題

毎回更新されてしまう

症状

このツールを実行するたびに毎回アクセス数が増えて更新日時が更新されてしまう。

しかも自分の全記事に対して。

更新判定のコードは入れている。以下のように。それでも毎回更新されてしまう。

  1. myArticles APIで最新データを取得する
  2. 更新日時updatedAtがDBのそれより新しいか判定する
    1. article APIで新しい本文を取得する
    2. DB更新する

(APIについてはモナレッジAPIを解析してみたを参照)

原因

モナレッジAPI仕様的に想定外の使い方をしているせいと思われる。更新ロジックを工夫する必要がありそう。

原因はおそらくモナレッジAPIの更新日時が更新されるタイミング。このツールでアクセスするたびアクセス数accessがインクリメントされ、更新日時もアクセス時刻に変わるような気がする。おそらく本文を取得するarticle APIを発行するたびに更新されるのだと思う。(APIのコードをちゃんと読んだわけではないので断定できない)。その結果、自分の全記事において毎回必ずarticle APIが発行されて更新日時やアクセス数も増えているのだと思う。

  • このツールでアクセス数がインクリメントされてしまう
  • アクセスするたび毎回更新日時がアップデートされてしまう
  • 更新日時では記事が変更されたか判断できない
    • 更新日時で記事の更新判定をすると必ず更新された判定になる
      • article APIを発行すると更新日時が更新される
      • よって毎回更新されたと判定されてarticle APIが発行される
    • 毎回全記事にarticle APIを発行してDBの本文と比較する
      • article APIを発行する時点で更新日時も変わってしまうので更新日時は本文の更新判定に使えない

更新ロジックをどうするか

これは困った。期待する動作としてはタイトル、本文、コメントに変化があるときだけ更新したかった。それ自体はSQLの条件式で可能だと思うが。問題はAPIをリクエストするたびアクセス数がインクリメントされて更新日時が変わることとと、サーバ負荷の高さ。

現状、サーバ負荷が高め。本当はmyArticles APIで更新があった記事かどうかを判断してからarticle APIで本文を取得したかった。たぶん本文のマークダウンは長いテキストで負荷も大きいから別APIなのだと思う。なのでサーバ負荷をさげるためにできるだけarticle APIの発行を減らしたい。そのためにはmyArticlesの段階で更新があったかどうか判定したかった。更新があったときだけ本文を取得するようにすれば、その分だけarticle APIの発行回数を減らせる。更新の有無は更新日時で判断できると思っていた。更新日時のほうはmyArticles APIで取得できるためarticle APIを発行せずに済む。

API仕様

でも、更新日時による本文の更新判定はできないようだ。APIの結果を見る限り、アクセスがあった時点で更新ありと判定されてしまうらしい。どうもarticle APIを発行した時点でアクセス数と一緒に更新日時もアップデートされるようだ。考えてみれば当然で、アクセスされたらアクセス数を増やすべきだし、アクセス数などの項目が更新されたら更新日時も更新されるのが自然。つまりこれがarticle APIの仕様なのだと思う。

更新日時では本文の更新判定ができない

本文を取得するためにarticle APIを発行したら更新日時が更新される。もし更新日時で本文が更新されるか判定していたら毎回更新されていると判断することになってしまう。でも最初は必ずarticle APIを発行して本文を取得する。その時点で更新日時が変わるから以降も更新ありと判断してしまい、ずっとarticle APIを発行することになってしまうという流れ。アカン。

更新日時では本文に変更があったかどうかを判定できない。ならばDBの本文とサーバの本文で差があるかで判断すればいいと思うが、それもダメ。なにせサーバから本文を取得するためにarticle APIを発行したら更新日時が更新されてしまうから。先述のとおり毎回article APIを発行する流れになってしまう。しかも自分の全記事に対して毎回行われるため、サーバ負荷も無用に高くなってしまう。現状のコードがまさにこれ。

本文に更新があったかどうか。この判定方法を、article APIを使わない方法で考える必要がありそうだ。そんなことが可能なのか? どんな処理パターンがありうるか最初から考えてみよう。

どうする?

  • 更新された記事のアップデートは諦める
    • DBに既存なら無視する
  • 毎回全記事を更新する
    • article APIを発行し、タイトル、本文、コメントに変化があればDBを更新する

更新された記事のアップデートは諦める

更新を諦めるなら簡単。問題は記事を変更してもそのバックアップがとれないこと。そんなのバックアップと言わない……。

毎回全記事を更新する

今回のやつ。

毎回全記事を更新すると、記事数が増えるほどムダが増えてしまう。たとえば最近一件だけ記事を追加したのでそれをバックアップするために実行したら、全記事のアクセス数がインクリメントされてしまう。これは絶対おかしい。目的外の影響を与えてしまっている。この副作用を完全になくすのは原理的に無理だとしても最小限に抑えたい。

タイトル更新されたら本文も更新する

妥協案としては、運用の工夫でカバーすることが考えられる。

  • 記事の本文を更新したらタイトルも変える(末尾に「追記」や「改稿」などをつける等)

タイトルはmyArticles APIで取得できるのでarticle APIを発行せずに済む。そこがポイント。

次のバックアップツールでは、もしタイトルが変わっていたら本文も変わっていると判断させる。このときはじめてarticle APIで本文を取得してDBを更新する。逆にタイトルが同じなら本文を更新対象外としarticle APIを発行しない。タイトル変化がどうあれ、アクセス数やモナなど他の項目はmyArticlesで取得できるので、値に変化があればそれぞれ更新する。

問題は、タイトルを変更し忘れたら変更後の本文をバックアップできないこと。ヒューマンエラーは確実に起こるので、いつか必ず絶対にヌケモレが発生する。あと、コメントもarticle APIでしか取得できないので、タイトルを更新したタイミングでしかコメントを更新できないことになる。それは論理的におかしいのだが、この際妥協する。自分で書いた記事だけバックアップできればいい。

妥協案が最も現実的でマシな方法かな?

対策案

思いつく方法3つをまとめると以下。

方法 問題
更新された記事のアップデートは諦める 更新後の記事がバックアップできない
毎回全記事を更新する バックアップするたび全記事のアクセス数や更新日時が更新される
記事の本文を更新したらタイトルも変える タイトルを更新し忘れたら更新後の本文がバックアップされない

どれも一長一短。今回は毎回全記事更新だったので副作用が大きくサーバ負荷も高い。次はこれを最小化すべくタイトルが更新されたときのみ本文を更新するようにしよう。