node-twitterでstreamAPIに再接続するときの実装

node-twitterを使用するとtweetはもちろんfilter/streamの取得も簡単に行えてテスト実行レベルでは何の問題もない。
ただ実際にBOTを稼働させてみるとたまにstreamが切断されて即時再接続を行おうとしてエラーコード420(速度制限)が返ってくる挙動がみられる。例えばアプリがクラッシュしたときにservice化などで自動再起動設定にしていて即時再接続を行うと、エラーコード420を延々繰り返してゾンビ化してしまう(しまった)。

解決策

twitterのドキュメントを調べてみると420が返ってきた場合(と他のエラーの場合)の再接続のベストプラクティスが示されているのでそれに従う。またstreamも一度にひとつだけ開くようにする。
  • Back off exponentially for HTTP 420 errors. Start with a 1 minute wait and double each attempt. Note that every HTTP 420 received increases the time you must wait until rate limiting will no longer will be in effect for your account.
HTTP 420 errorの場合インターバルを置き再々接続までの時間を最大値まで指数関数的に増やすようにと指示されている。(例:60秒,120秒,240秒,…3600秒)
node-twitterでは実装の例などは無いので自分で実装する必要があるが、issue/159にコードの例があり参考にできる。 https://github.com/desmondmorris/node-twitter/issues/159
ただしここにおけるstream destroy()の問題は解決しているようだしsetTimeout()もPromiseでラップして書きたいので書き直してみた。

実装

‘use strict’;
const Twitter = require(‘twitter’);
  const client = new Twitter({
    consumer_key:process.env.TWITTER_CONSUMER_KEY,
    consumer_secret:process.env.TWITTER_CONSUMER_SECRET,
    access_token_key:process.env.TWITTER_ACCESS_TOKEN_KEY,
    access_token_secret:process.env.TWITTER_ACCESS_TOKEN_SECRET
});
var timeintervalsec = 1;
var exponent = 0;
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
// インターバルを60* 2^0SEC,60*2^1SEC,60*2^2SEC…としてmain taskを実行する実装
function main() {
    timeintervalsec=60*Math.pow(2, exponent);
    wait(timeintervalsec*1000)
        .then(() => {
            letdate=newDate();
            console.log(date+’ Reconnecting… Interval Time = ‘+timeintervalsec+” sec”)
            varstream=client.stream(‘statuses/filter’, { track:’something’ });
            stream.on(‘data’, function (event) {
                exponent=0; //初期化
                console.log(‘Maintask’);
            });
            stream.on(‘error’, function (error) {
                if (exponent>6) { // exponentが6以上つまり 2^12=3840 > 3600sec=1hourの場合処理を終了する
                    console.log(‘再接続間隔が1時間以上となったため終了’);
                    throwerror;
                } else if (error.message == ‘Status Code: 420’) { // 420の場合はインターバルを増やして再接続する
                    stream.destroy(); //多重起動を防止するため
                    exponent++;
                    main();
                } else {
                    console.log(‘420以外のエラーによる終了’);
                    throwerror;
                }
            });
        })
        .catch();
};
main();
(recconect.js)

議論

  • コードは420の場合のみなので他のエラーの場合も実装してもいいかもしれないが、素直にクラッシュさせたほうがいいと思ってこのまま使用していたりする。
  • twitterから420が返ってこないとテストできない。そこで邪道かもしれないがソースコードの420のところを401にしてKEYを一部変更して起動するとtwitterから401 errorが返ってくるので指数関数的に間隔をあけて、けなげに再接続を試みているのを観察できる。わざと420を起こすのも迷惑だしどうするのが一番いいかな?

参考

作成したBOT。twitterで指定のキーワードをつぶやくと仮想通貨を投げてくれる。
ソースコード
似たような問題に対処してる方

Sequelizeで”Please use Symbol based operators…”のエラーが消えないときの対処方法

Sequelizeを読み込んだときに以下のエラーメッセージが出ることがあります。

“String based operators are now deprecated.Please use Symbol based operators for better security ….node_modules/sequelize/lib/sequelize.js:236:13”

内容としては「where句でStringのオペレーターではなくSymbolのオペレーターを使いましょう、その方がセキュリティ的にベターだからね。」という内容なのですが、where句にオペレーターを使用していない場合でも出てきてしまい困っていました。

解決方法を見つけました。

https://stackoverflow.com/questions/46608382/sequelize-deprecated-error-message?rq=1

const Sequelize = require('sequelize')
const sequelize = new Sequelize(
  DB_NAME,
  USERNAME, 
  PASSWORD,
  {
    host: HOSTNAME,
    dialect: 'mysql',
    logging: false,
    freezeTableName: true,
    operatorsAliases: false
  }
)

読み込む際に operatorsAliases: false を指定しておけばよかったんですね。これはわからん。

Stringが廃止されたらエラーメッセージもなくなるのでこういう工夫も不要になるでしょう。

終わり

Ubuntu16.04を18.04にアップデートした際のトラブルと対処したこと

ubuntu16.04LTSを使用していたのですが、やっと安定版の18.04LTSが正式リリースされたのでアップデートしました。ターミナルから18.04に無事アップデートできたので、指示どおり再起動したところ、トラブルが発生。

アカウントの選択とパスワードの入力までは可能なのですが、パスワードを入力してログインしようとした瞬間にフリーズするという不具合でした。

まあよくあるよねと思いつつ、なんとか自力で立ち上げられるところまで来たのでメモ。

条件

・ubuntu16.04LTSからubuntu17.10にアップデートしてすぐにubuntu18.04LTSにアップデートしたときに発生。

・win10とubuntuのディアルブート環境

マシンの構成がprocessor Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
GPU [GeForce GTX 1050 Mobile] だったので、GPUのドライバ辺りだろうなあと思い結果的にNIVIDIAのドライバからubuntuの標準のドライバに戻したりいろいろしたら入れるようになった。

試したこと

以前のカーネルのバージョンで起動してみる。

Advanced options for ubuntuからインストールされている古いカーネルのバージョンで起動できる。以前これで行けたので試してみたが起動せず。

recovery modeで起動

現時点で最新のバージョンのリカバリーモード4.15.0-20-generic(recovery mode)で起動した。

リカバリーモードには入れたのでdpkgとgrubでパッケージとブートローダーをアップデート。

resumeで低解像度モードで起動。入れたのでとりあえずターミナルから、

$sudo apt-get update

$sudo apt-get upgrade

その後「ソフトウェアとアップデート」からGPUのドライバをubuntuの標準のやつに戻しました。

その後また低解像度モードでログインしてドライバを確認した画像↓

この後再起動すると通常のモードでログインできました。

いろいろやったので何が原因なのかはっきりわからなかったんですが、多分NIVIDIAのGPUドライバだと思います(偏見)。

ダウングレードするハメにならなくてよかったです。

終わり。