モヒカンメモ

髪色が定期的に変わることに定評のある(比較的)若者Webエンジニアの備忘録

Matz氏による「若手エンジニアの生存戦略」勉強会に行って来た #colab_matz

f:id:pinkumohikan:20170611160204p:plain

エンジニアにはお馴染みRubyのパパ、Matz氏による「若手エンジニアの生存戦略」勉強会に行って来た

講師: まつもとゆきひろ 提供: サポーターズ 特別協賛: Speee 会場提供: DRECOM

f:id:pinkumohikan:20170611160214j:plain ↑ 会場設営中の様子。素敵な空間でした!DRECOMさんありがとうございます!


公演内容

先にブログを書いた人たちがいい感じにまとめてくれているので、そちらに譲りますw

あとは、 ハッシュタグ #colab_matz で、会場の雰囲気の8割ぐらいは伝わりそう

https://twitter.com/search?f=tweets&vertical=default&q=%23colab_matz&src=tyah


特に刺激を受けた / 共感したところをpick up

基本的な考え方

  • 生き残ること = 死なないこと

シリコンバレーで成功した人達の共通点

  • 知能の高さより、パターン認識
  • 成功者の猿真似をしても、同じように成功はできない
    • 「こういうシチュエーションだったら、あの時のアプローチが使えるんじゃないか!?」って考えるようにする

我慢に価値はない

  • "我々は我慢しがちだが、我慢することに価値はない。"
    • e.g. 客に合わないのにスーツを着る、新人だからという理由でアレな待遇を受ける、etc...
    • 事なかれ主義、「俺も我慢してるんだから、お前も我慢しろ」系は、すなわち ただの理不尽
      • 理不尽は拒否しなければならない
  • 我々は会社に養われている訳ではない
    • 労働は我慢の対価ではなく、提供したvalueに対する対価
      • valueを高めることだけ、追い求めるべき
      • ~~言いたいことも言えない、そんな世の中じゃポイズン〜~~
  • (会社の) 言うことを聞かないと捨てられるか?
    • 雇用者側は、そんなに簡単には (労働者を) 捨てられない
    • 我々の業界には、転職先はいくらでもある

インプットとアウトプット

  • 大前提として、インプットは必要
  • インプットはみんなやっているので、 他者と差別化しにくい
  • ではどうするか?
    • アウトプット頑張る
      • e.g. ブログ、OSS活動、etc...
      • 怖い人からツッコミを受けても、それも成長機会

No deal、という選択


感想

世界的に有名な日本人エンジニアが考える生存戦略ってどんなのだろう?と思って参加したが、文字通りの「生き残るには」というテーマで安心した (「グローバルに活躍できる超すごいエリートになるには!」みたいなテーマだったらどうしようかと思ったw)

自分もどちらかと言うと「我が道を行く」スタイルなのでMatz氏の考え方に似ているのか、基本「分かる分かる」と思いながら聞いていたが、 我慢に価値はない という話が特に刺さった

自分の周りの知人でも、客に合わないのにスーツ着用の強要や、ヘアカラー禁止、謎文化の強制で疲弊している人を数人見ていたので、「そういうの必要なの?仕事の効率やモチベーション、それによって落ちてない?」と思っていた矢先で、タイムリーだった。話を聞いてすぐ、数人にその話をシェアしたw

あとは、、、

参加者が自発的にこういう働き掛けをするイベントって正直見たことがなかったので、素直にいいなあと思った (その後に予定があって参加できなかったのが悔やまれる)

CentOSへaws-cliを入れようとしたらPython.h,pyconfig.hが無いって怒られた

f:id:pinkumohikan:20170611155843p:plain

CentOSへpipでaws-cliを入れようとしたら、下記のようなエラーが出た。

$ pip install aws
Collecting aws
  Downloading aws-0.2.5.tar.gz

...snip...

  No package 'libffi' found
  c/_cffi_backend.c:2:20: 致命的エラー: Python.h: そのようなファイルやディレクトリはありません
   #include <Python.h>
                      ^
  コンパイルを停止しました。

...snip...

  gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -fPIC -I/usr/include/python2.7 -c build/temp.linux-x86_64-2.7/_openssl.c -o build/temp.linux-x86_64-2.7/build/temp.linux-x86_64-2.7/_openssl.o
  build/temp.linux-x86_64-2.7/_openssl.c:12:24: 致命的エラー: pyconfig.h: そのようなファイルやディレクトリはありません
   #  include <pyconfig.h>
                          ^
  コンパイルを停止しました。
  error: command 'gcc' failed with exit status 1

aws-cliの依存パッケージが足りないから自動で追加インストールしようとしたけど、Python.h, pyconfig.hが無くてコンパイルに失敗している模様

パッケージインストール中に出る「○○が無い」系のエラーは、単純に足りないパッケージをインストールしてあげれば機嫌が治るはず

今回は名前からして、いかにもpythonのヘッダファイルが足りないっぽいので python-devel をインストールしてみる (補足: CentOSで言う *-devel 系のパッケージは開発者向けのアレコレが入ったもの。今回足りないって言われているヘッダファイルも、コンパイル時しか要らないのでこっちに入っているはず)

$ sudo yum install python-devel
読み込んだプラグイン:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * base: ftp.iij.ad.jp
 * extras: ftp.iij.ad.jp
 * updates: ftp.iij.ad.jp
依存性の解決をしています
--> トランザクションの確認を実行しています。
---> パッケージ python-devel.x86_64 0:2.7.5-48.el7 を インストール
--> 依存性解決を終了しました。

依存性を解決しました

======================================================================================================================
 Package                        アーキテクチャー         バージョン                      リポジトリー            容量
======================================================================================================================
インストール中:
 python-devel                   x86_64                   2.7.5-48.el7                    base                   393 k

トランザクションの要約
======================================================================================================================
インストール  1 パッケージ

総ダウンロード容量: 393 k
インストール容量: 1.0 M
Is this ok [y/d/N]: y
Downloading packages:
python-devel-2.7.5-48.el7.x86_64.rpm                                                           | 393 kB  00:00:00
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  インストール中          : python-devel-2.7.5-48.el7.x86_64                                                      1/1
  検証中                  : python-devel-2.7.5-48.el7.x86_64                                                      1/1

インストール:
  python-devel.x86_64 0:2.7.5-48.el7

完了しました!

いざ、リトライ

$ pip install aws
Collecting aws

...snip...

Successfully built cryptography
Installing collected packages: prettytable, cryptography, paramiko, fabric, aws
Successfully installed aws-0.2.5 cryptography-1.7.2 fabric-1.13.1 paramiko-2.1.2 prettytable-0.7.2

今度はちゃんとコンパイルが通って、インストール完了したっぽい

$ aws --version
aws-cli/1.11.82 Python/2.7.5 Linux/3.10.0-514.16.1.el7.x86_64 botocore/1.5.45

わーい、aws-cli入ってる

正規表現 "?:" の意味 #些細な検証メモ

f:id:pinkumohikan:20170611155505j:plain

正規表現って構文が色々あって、読むごとに学びがありますよね。 今回は “?:” っていう構文について調べたので、備忘録がてらメモっときます

(写真は昨日の夜に行った 七志 とんこつ編 渋谷道玄坂店 です)

正規表現 “?:” の意味

MSDNによると、

(?:pattern) pattern と一致しても、その文字列は記憶されず、後で使用することはできません。"or" を意味する (|) を使用してパターンの一部を結合するときに便利です。たとえば、'industry|industries' と指定する代わりに、'industr(?:y|ies)‘ と指定する方が簡単です。

https://msdn.microsoft.com/ja-jp/library/cc392020.aspx

とのこと

要は、「 () のようにマッチングには使えるが、マッチした文字列の情報を保持しないよ」っていう表現ですね

“?:” は、どういう時に使うのか?

  • メモリを節約したいとき
  • この部分は要らない、っていうのを読んだ人に伝えたいとき

たとえば

Gmailメールアドレスから、エイリアスの部分を除く文字列を取得したいとき、何も考えないと下記のような表現になると思います

([a-zA-Z0-9-\.]+)(\+[a-zA-Z0-9-\.]+)?@gmail\.com

↑に対して example+aaa@gmail.com っていう文字列を与えると、example+aaa の2つがマッチング結果になります。 欲しかったのは example だけなので +aaa は余計です。

こういう時に “?:” を使って下記のように書くと example だけを求められます。

([a-zA-Z0-9-\.]+)(?:\+[a-zA-Z0-9-\.]+)?@gmail\.com

sudoコマンドをパスワードなしで使えるようにする

f:id:pinkumohikan:20170611155236p:plain

unix系OSには任意のユーザとしてコマンドを実行する sudo というコマンドがあります。

Linuxコマンド集 - 【 sudo 】指定したユーザーでコマンドを実行する:ITpro

このコマンドは、一般ユーザでログインした状態でroot権限でコマンドを実行したいときに良く使われますが、デフォルトだと下記のようにユーザのパスワードを要求されます。

$ sudo whoami
[sudo] password for h-shinoda:
root # rootユーザとして実行されている

プロビジョニング時など、何度もsudoを叩く必要があるときに都度パスワードを要求されるとキレそうになりますが、下記のように設定をすることによってパスワードを要求させないように出来ます。

注意 パスワードが要求されない = その一般ユーザアカウントがハックされる = rootが乗っ取られると同義なので、リスクを承知のうえで行って下さい。

方法

誰がどのようにsudo出来るかという情報は /etc/sudoers というファイルで管理されています。が、このファイルを決して手動で編集してはいけません (詳しくは最下部の補足を参照)。

今回やろうとしている変更は「特定のユーザに対して、パスワードなしで(すべてのコマンドを)sudoできるようにする」というものですので、ユーザ名を h-shinoda とすると、追加する設定としては下記のようになります。

h-shinoda ALL=(ALL) NOPASSWD: ALL

上記を visudo コマンドを使って /etc/sudoers へ設定します

$ sudo visudo

90行目辺りのrootユーザに対する設定の直下辺りにでも追加します。

## Allow root to run any commands anywhere
root    ALL=(ALL)       ALL
h-shinoda ALL=(ALL) NOPASSWD: ALL # コイツを追加

追加したら (vimの場合) :wq で write & quit しますが、この時、設定ファイルとしてのvalidationが走り、問題なければ /etc/sudoers へ変更が適用されます。

ここまで設定できれば、sudoでパスワードを要求されることはありません!

$ sudo whoami
root

おつかれさまでした!

補足

補足1: どうして手動で /etc/sudoers を編集してはいけないのか

sudoコマンドを実行するたびにsudoersファイルが読まれるため、変更は即時反映されます。 つまり、誤った設定や構文エラーなどで一度設定を壊してしまうとそれ以降sudoコマンドが使えなくなりますΣ

インターネットへ公開されているサーバの場合、セキュリティ的に普通はrootによるssh接続を禁止していると思うので、その状況でsudoが使えないとなると... 考えるだけでもゾクゾクしますね!(遠い目)

補足2: 実際には /etc/sudoers はあまりイジらない

一つのファイルにいろんな設定を書くと、可読性が下がってしまいます。 じゃあどうするでいうと /etc/sudoers.d 下に、ユーザごとに設定ファイルを持つことが多いと思います。

その場合、visudoコマンドの-fオプションでファイル名を指定します。

$ sudo visudo -f /etc/sudoers.d/h-shinoda

補足3: visudoで誤った設定を保存してしまった場合

問題があれば下記のようにでます。

$ sudo visudo
visudo: >>> /etc/sudoers: syntax error near line 92 <<<
What now?

この時の選択肢としては、下記の3つがあります。

  • e: 再編集する (再度vimが開き、問題点を直す)
  • x: sudoersを変更をせずに終了する
  • q: 構わずsudoersを変更をして終了する (DANGER!)

補足4: アレェ?設定したのにパスワードが要求される...

ユーザが wheel グループに属している場合、 wheel に対する設定のほうが後に書かれているのでNOPASSWD指定が上書きされて、パスワードが要求されてしまいます。 自分が wheel グループに属しているかどうかは id コマンドで確認できます。

$ id
uid=1000(h-shinoda) gid=100(users) groups=100(users),10(wheel)

この場合の選択肢としては、下記のようなものがあります。

  1. wheel グループからユーザを外す
    • usermod コマンドを使って wheel グループから外す
  2. 追加したNOPASSWDの設定を、sudoersの %wheel ALL=(ALL) ALL 行以降へ移動させる

`set -e` していても `command1 || command2` みたいな書き方ができる #些細な検証メモ

f:id:pinkumohikan:20170611154955p:plain

ことのはじまり

シェルスクリプトを書くときに set -e しますよね

これをしておくとシェルスクリプトの途中でエラーが発生した場合に処理を中断してくれるので、ある処理が失敗したあと、意図せず後続の処理が動いて悲劇が起こるのを防いてくれます

ところが先日、 「set -eしちゃうと、command1 || command2 みたいな書き方が出来なくてアレ」 みたいな記事を目にしたので、そんなことあらへんやろと検証してみました (100%の自信はなかったので、念のためにw)

結論

set -e していても command1 || command2 みたいな書き方ができる

検証

下記のようなそれっぽいスクリプトを用意して

set -ex

echo 'hoge'

# ここで中断されずに `echo 'yay'` が実行されて欲しい
mkdir /home || echo 'yay'

# ここで非ゼロexit codeとなるので、中断されるはず
mkdir /tmp

# ここまで行くと行き過ぎ
echo 'Error! Should not be call this line!' && exit 1

動かすと、、、

$ bash hoge.sh
+ echo hoge
hoge
+ mkdir /home
mkdir: cannot create directory ‘/home’: File exists
+ echo yay
yay
+ mkdir /tmp
mkdir: cannot create directory ‘/tmp’: File exists

最初のmkdirに失敗しても、or実行しているechoに成功しているので 中断されていません もちろん、2度目のmkdirでは普通に非ゼロのexitとなったので中断されました

上記の検証で、 set -eしている状態で前者のor演算が失敗しても処理は中断されない ことがハッキリしたので、set -e していても臆せず command1 || command2 みたいな書き方ができますね

アイキャッチ画像は いらすとや からお借りしました

PHPでマジックメソッド __toString 内で例外を投げるとfatalになる #PHPの不思議な世界

f:id:pinkumohikan:20170611154720p:plain

はじめに

PHPは素敵な言語です。

※ 「素敵」という表現は主観的なものであり、何を素敵と思うかは個々の自由である。そのため異論は一切認めない。

この #PHPの不思議な世界 という謎のタグの付いている記事は、職業プログラマーとしてPHPコードを書いていて「ふぇー」と思った仕様や、「ふーん」と思った仕様についてダラダラとコードと感想を書いていくコーナーです。 PHPをdisる意図は決してなく、日頃ご飯を食べさせて頂いているPHP様には足を向けて寝ることはできません。

お題

マジックメソッド __toString 内で例外を投げるとfatalになる

前説

PHPには、マジックメソッドと呼ばれる仕組みが存在します。

特定の条件が満たされた時に、明示的にcallしなくても勝手に規定のメソッドがcallされます。 メソッド名の先頭がアンダースコア二つ __ で始まっているのが特徴です。

PHP: マジックメソッド - Manual

解説

マジックメソッド toString は、文字列化メソッドです。 文字列が期待されている関数やメソッドに対してオブジェクトを渡した際に、 toString が呼ばれます。

スキームとドメイン、パスしかないシンプルなURLクラスを考えます。

class Url
{
    private $scheme;
    private $domain;
    private $path;

    public function __construct($scheme, $domain, $path)
    {
        $this->scheme = $scheme;
        $this->domain = $domain;
        $this->path = $path;
    }

    public function __toString()
    {
        return sprintf(
            '%s://%s/%s',
            $this->scheme,
            $this->domain,
            $this->path
        );
    }
}

上記クラスをnewしてインスタンスを生成し、引数として文字列を期待する関数に渡してあげると、 __toString メソッドで定義した方法で文字列化して出力されます。

$ php -a
Interactive shell

php > print PHP_VERSION;
5.6.29
php >
php > require_once 'Url.php';
php > $url = new Url('https', 'pinkumohikan.com', 'feed');
php > print $url;
https://pinkumohikan.com/feed

本題

一言で言うと表題の通りで、「マジックメソッド __toString 内で例外を投げると、fatal errorになります」。

※ __toString はインスタンスが保持しているデータを文字列化する方法を定義するためのマジックメソッドなので、例外を投げたくなることは普通はありません。というかそういう設計をしてはいけません。振りではありません。

試しに、先ほどのUrlクラスの __toString メソッドを、仕事をせずに例外を投げるだけのゴミみたいな実装に書き換えたFreedomUrlクラスを用意します。

class FreedomUrl
{
    private $scheme;
    private $domain;
    private $path;

    public function __construct($scheme, $domain, $path)
    {
        $this->scheme = $scheme;
        $this->domain = $domain;
        $this->path = $path;
    }

    public function __toString()
    {
        throw new \RuntimeException('I am freedom!!!!');
    }
}

実行してみます。

$ php -a
Interactive shell

php > print PHP_VERSION;
5.6.29
php >
php > require_once 'FreedomUrl.php';
php > $url = new FreedomUrl('https', 'pinkumohikan.com', 'feed');
php > print $url;
PHP Fatal error:  Method FreedomUrl::__toString() must not throw an exception in php shell code on line 0
PHP Stack trace:
PHP   1. {main}() php shell code:0

Method FreedomUrl::__toString() must not throw an exception と、非常にお怒りです。

上位でtry ~ catch構文で拾おうとしてもダメです。 see __toString()でExceptionが発生すると死ぬ - Qiita

このことはPHPリファレンスの __toString メソッドの説明 にも、「絶対例外投げんなよ!絶対だぞ!」と書かれています。

警告 __toString() メソッド内から例外を投げることはできません。そうした場合、致命的なエラーが発生します。

こういうエラーを見たら、おそらく __toString メソッドが「オブジェクトが持つデータの文字列化」以上の責務を負っていると思われるので、是非設計を見直してあげて下さい。

MySQLのtimestamp型カラムにUTC1970年1月1日 00:00:00よりも前の日時を入れようとしてハマった

f:id:pinkumohikan:20170611145446p:plain

timestamp=0よりも前の日時をinsertしようとして怒られたときの備忘録

できごと

いつものように怪しいWebアプリ作りに勤しんでいたら、とある怪しいWebアプリがエラーを吐いていた

Next Illuminate\Database\QueryException: SQLSTATE[22007]: Invalid datetime format: 1292 Incorrect datetime value: '1970-01-01 07:30:17' for column 'xxx_at' at row 1 (SQL: xxx) in /web/xxx/vendor/laravel/framework/src/Illuminate/Database/Connection.php:770

データベースへレコードをinsertしようとしたときにエラーが発生し、LaravelのDBライブラリから例外が投げられてバッチがお亡くなりになっている模様

こうしてブログネタが見つかった、めでたしめでたし (w)

デバッグする

最初は Invalid datetime format と言われているので、バグか何かで日付として不正な値をinsertしようとしてエラーになったのかな?と思ってデータを確認した

1970-01-01 07:30:17

が、日付をよく見ても特に気になるところは無し 問題のない Y-m-d H:i:s 形式 (通称: 山田形式 )。ちゃんとパースできるはず

次に気になるのが 1970-01-01 と言う部分

ピンと来ました? Unixtimeが始まった日です

日時まで見て見ると

1970-01-01 07:30:17

なるほど07:30、 すき家で朝食メニューが頼める時間

原因は完全にコレで、

php > print date_default_timezone_get();
Asia/Tokyo
php > print date('Y-m-d H:i:s', 0);
1970-01-01 09:00:00

UNIX Timestamp 0 = JST 1970-01-01 09:00:00

つまり、 JST 1970-01-01 09:00:00よりも前はUNIX Timestampでは表現できない

とても当たり前だが、普段はJSTはあまり考えないので失念していた

対応を考える

テーブルには 1970-01-01 07:30:17 と言う日付をinsertしたいのに、カラム型の制約で Invalid datetime format と言われてしまう

パッと思いつく対応としては、

  1. 1/1 9時よりも前の日時を、一律9時きっかりに丸める
  2. 1/1 9時よりも前の日時を扱えるカラム型に変更する

といったもので、それぞれ

1: ビジネス的にそれが許されるなら、と言う事前条件がある 2: (ディスク容量的な意味で)データ量が増える

という難点がある

数千万件、数億件オーダのデータを扱うなら4バイト -> 8バイトへの変更は気になるだろうが、今回の場合は高々数百万件程度なので気にせずdatetime型へ変えてしまう

補足

今回リファレンスをちゃんと読むまで知らなかったのだが、timestamp型、datetime型カラムのデータ保持に必要なディスク容量が MySQL 5.6.4を境に 変わっていたΣ

詳しくはsee MySQL :: MySQL 5.6 リファレンスマニュアル :: 11.7 データ型のストレージ要件

対応する

問題のカラムをtimestamp型からdatetime型へ変更する

alter table some_table modify xxx_at datetime not null;
  • Laravel Migration Fileだと
Schema::table('some_table', function (Blueprint $table) {
    $table->datetime('xxx_at')->change();
});

今回のアプリを動かしているデータベースサーバはMySQL5.6以上なので、オンラインで上記DDLを実行しても他からの読み書きはブロックされない

see MySQL :: MySQL 5.6 リファレンスマニュアル :: 14.11.1 オンライン DDL の概要

5.6未満のMySQLを運用中で人生が辛いですか? We’re hiring!

まとめ

  • JST 1970-01-01 09:00:00よりも前の日時はtimestamp型では扱えない
    • 1970年1月1日前後の日付でエラーが出たら、timestamp型問題を疑ってみる
  • どうしても扱いたかったらtimestamp型ではなくdatetime型を使う
  • カラムの型変更はMySQL5.6以上ならノンブロッキングに実行可能

余談

本文中では触れていませんがデバッグの過程で、テーブルの型定義とアプリから入れようとしている日付のフォーマットが違っていて怒られているのかと思って調べていたら、自称エンジニアな方が

そういう時は set SQL_MODE = ''; すればいいんだよ!

って言っているのをいくつか見かけて、とても強く生きたい気持ちになった

一方で、SQLモードについて詳しく解説してくださっている記事も見つけて、MySQLについて学びがあった

MySQLの自動変換を丁重にお断りするためのたった1種類の呪文 - sakaikの日々雑感~(T)編

感謝++

WordPressの予約投稿が仕事しなかったのでcronの設定をした

WordPressの予約投稿がきちんと動かずに、「投稿失敗」してしまったのでその原因調査と対応の備忘録を残しておく

できごと

WordPressでブログを構築して翌日公開の予約投稿を設定したが、時間になっても公開されなかった (記事が公開されるとJetPackのパブリサイズ機能によってSNSに記事公開の通知がされるはずだが、所定時間を過ぎても通知されていなかった)。

なぜだろうと思い、ブログにアクセスしたところ予約投稿が公開された (SNSへ記事公開の通知がされた)

原因調査

「wordpress 予約投稿 失敗」みたいなキーワードでググって色々調べた結果、デフォルトのWordPressの設定だと、

ブログへのアクセスをトリガーに

  1. 予約投稿チェック
  2. 公開時刻を過ぎている記事があればそれを公開

という動きをする模様 (コードレベルではまだ追っていない)

今回の場合は、投稿が公開される時間帯にアクセスが全然無かったため、所定時間を過ぎても公開されなかった、と言うのが原因のよう

確かにそれだったら

なぜだろうと思い、ブログにアクセスしたところ予約投稿が公開された」

という事象にも説明がつく

レンタルサーバとかで動かすことを考えたらcronが使えない環境でも動く必要があって、それにはアクセスをトリガーにするしかないよな、と設計的にも理解できる

ただ、これはこれでアクセスのたびに予約投稿があるかをDBに問い合わせているはずで、それで発生するオーバーヘッドはブログへのアクセス数が大きくなっていくにつれて無視出来ない大きさになるのでは?という疑念が生まれる

対応

結論としては アクセスをトリガーにしない設定を追加 + cronでwp-cron.phpを定期実行させる という対応をした

それによって、アクセスをトリガーに動かなくなるのでアクセスのたびに余計なオーバヘッドが乗ることもないし、アクセスがないから予約投稿が公開されない、、、と言う悲劇も起こらない (はず)

1. wp-config.phpへ設定

wp-config.phpの最下部あたりに、下記を追加する

define('DISABLE_WP_CRON', 'true');

これで、アクセスをトリガーに動かなくなる (はず)

2. crontabへ設定

cron = 指定した時間 or 一定周期で自動的にプログラムを動かす仕組み crontab = cronの設定

crontab -e でcrontab設定ファイルを開き、下記を追加

* * * * * curl -sS https://pinkumohikan.com/wp-cron.php

上記の意味としては、毎分curlと言うコマンドを使って https://pinkumohikan.com/wp-cron.php に対してアクセスするよ、って感じ ローカルなんだから直接 php /path/to/wp/wp-cron.php を叩けばいいじゃんっていうのも分かるけど、実行ユーザとディレクトリ構成に強く依存しちゃうのが好みじゃないので今回はこれで。

参考資料

  1. 意外と知らない、WordPressの正しいcronの設定方法|ある蜜柑の上にアルミ缶。
  2. WordPressの予約投稿が失敗した時に試す4つの解決策 | OXY NOTES