1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

自動投稿スクリプトの「二重投稿」を、ローカルの記録に頼らずAPI側で防いだ話

1
Posted at

この記事の要点(TL;DR)

  • 記事を自動で投稿する小さなスクリプトを Node.js で作りました。
  • 最初は「投稿したかどうか」をローカルのファイルに記録して二重投稿を防ごうとしたのですが、これは意外とすぐ壊れます。
  • 結論:「投稿済みかどうか」の判断は、投稿先(今回はQiita API)の状態を正として確認するのがいちばん安全でした。
  • キーワードは 冪等性(べきとうせい)。「同じ操作を2回やっても、結果が1回ぶんにしかならない」性質のことです。

まず用語を軽く補足します。APIは「プログラムから外部サービスを操作するための窓口」のこと。ここではQiitaが用意している「記事を投稿する」「自分の投稿一覧を取る」といった窓口を使います。

何をしたかったか

個人開発で、書きためた記事を1日1本、自動で投稿する仕組みを作っていました。やることはシンプルで、

  1. 記事ファイルを読む
  2. Qiitaに投稿する
  3. 投稿できたらURLを記録する

だけです。ところが、この「自動」というのが曲者でした。

最初の失敗:ローカルファイルで「投稿済み」を管理した

最初はこう考えました。

投稿したら posted.json みたいなファイルに「この記事はもう出した」と書いておく。次に実行するときはそれを見て、投稿済みならスキップすればいい。

一見よさそうですが、これは次のようなときに簡単に事故ります。

  • 投稿はできたのに、記録を書く前にプログラムが落ちた → 記録には「未投稿」と残る → 次に実行するともう一度投稿してしまう(=二重投稿)
  • 別のPCやアカウントで動かしたら、ローカルの記録ファイルが無くてまた投稿してしまう

つまり「自分のローカルの記録」と「実際にQiitaにある記事」がズレるとアウトなのに、この方式は簡単にズレてしまうのです。

直し方:投稿先(API)の状態を「正」にする

そこで発想を変えました。「もう投稿したか?」は、自分のメモではなく、投稿先に聞けばいいのです。

Qiitaには「自分の投稿一覧を取得する」APIがあります。投稿する前にまずこれを叩いて、

  1. 同じタイトルの記事がすでにあるか? → あるなら中止
  2. 今日すでに何か投稿していないか?(1日1本ルール)→ しているなら中止

をチェックします。こうすると、たとえ前回スクリプトが途中で落ちていても、実際にQiitaに記事がある以上「投稿済み」と正しく判断できるので、二重投稿になりません。

実際のコードはこんな感じです(要点だけ)。

// 1. 投稿する前に、自分の投稿一覧をAPIから取得する
const res = await fetch(
  "https://qiita.com/api/v2/authenticated_user/items?page=1&per_page=20",
  { headers }
);
const items = await res.json();

// 2. 同じタイトルが既にあれば中止
if (items.find(it => it.title === title)) {
  console.error("SKIP: 同じタイトルの記事が投稿済み");
  process.exit(2);
}

// 3. 今日すでに投稿していれば中止(1日1本)
const today = new Date().toLocaleDateString("sv-SE", { timeZone: "Asia/Tokyo" });
if (items.find(it => it.created_at.startsWith(today))) {
  console.error("SKIP: 今日はすでに投稿済み");
  process.exit(2);
}

// ここまで来たら、はじめて投稿する

ポイントは「ローカルに何も覚えさせない」ことです。判断材料をすべてAPI(=本当の状態)から取ってくるので、記録ファイルの管理ミスやクラッシュに影響されません。

もうひとつの工夫:投稿の直後に記録を更新する

それでも「投稿した瞬間にクラッシュしたら?」という不安は残ります。ここは2段構えにしました。

  • 投稿が成功したら、すぐに記事ファイルの状態を posted に書き換える(クラッシュしうる時間を最短にする)
  • 万が一その書き換え前に落ちても、次回はAPIチェックで「同じタイトルあり」と弾かれるので結局二重投稿にならない

「メインの安全装置(APIチェック)」+「補助のメモ更新」という順番です。メモはあくまで補助で、正解は常にAPI側にある、という設計にしておくと安心でした。

学び:冪等性は「記録」より「事実」で担保する

今回いちばんの学びは、二重実行を防ぎたいときは、自分のローカルの記録に頼らず、操作対象の“事実”を確認しにいくということでした。

  • ローカルの記録は、ズレる・消える・壊れる
  • 投稿先の状態は、いつ確認しても本当のこと

自動化スクリプトは「途中で落ちる」「二重で走る」が普通に起きます。そのときに壊れないかどうかは、「何を正としてチェックしているか」で決まるんだなと実感しました。個人開発で小さな自動化を作るときの参考になれば嬉しいです。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?