Discord botからFirebase Realtime Databaseに書き込んでWebで読み込むとどれぐらい遅延が発生するのかざっくり調べた

この記事は[Firebase #2 Advent Calendar 2018](https://qiita.com/advent-calendar/2018/firebase-2)の25日目（最終日）の記事になります。

気がついたらカレンダーの記事がすべて埋まっていましたね。ありがとうございます。

この記事で締めになりますが、そんなことは一切気にせず個人的に知りたかった範囲で収めた趣味的な調査記事を書きます。

## 概要

Discordのボイスチャンネルを監視しているbotが、入室者の音声のON/OFFをFirebase Realtime Databaseに書き込むとどれくらい遅延があるのかを、実際に使用したコードとその結果（動画）を共有する記事になります。

## なぜこんなものを？

1. 以前からRealtime Databaseへの書き込みからの遅延がどの程度あるのかが気になっていた
2. [技術系podcastノウハウ本](https://booth.pm/ja/items/828339)に書かれていたdiscord botの「ボイスチャットで各個人が喋ったり黙るのをトリガに動作」という処理が面白そうで何かやってみたかった

という2点が主な理由です。あくまでも個人的に興味があって知りたい範囲内での調査です。

Realtime Databaseの書き込みの遅延はゲームで使えないレベルにはある、という話を以前から聞いたことがあったのですが、具体的にどれくらいあるのかを知りたくてやってみました。

## この記事を読むのに向かない人

- 徹底した遅延速度を数字レベルで知りたい人
- 大規模人数によるアクセス、一度に大量のデータを送り込んだ際の遅延を知りたい人

本当にガバガバな調査内容なので、しっかりした調査結果を望む方には向きません。

## 利用コード

### 書き込み側Discord bot

参考の同人誌に書かれていたコード（[discord.js](https://discord.js.org/)を利用）をベースにして、さっくりと作成しました。botの設定やfirebaseの設定方法などはすべて省略します。

```js:index.js
const Discord = require('discord.js')
const admin = require("firebase-admin");
const client = new Discord.Client()

var getFirebaseDatabase = function() {
    let serviceAccount = require(`./serviceAccountKey.json`)
    admin.initializeApp({
        credential: admin.credential.cert(serviceAccount),
        databaseURL: "https://{project-id}.firebaseio.com/"
    })
    return admin.database()
}

client.on('ready', () => {
    console.log(`Logged in as ${client.user.tag}!`)
});

client.on('message', msg => {
    const channelName = 'xxxx'

    if (msg.content === 'rec') {
        msg.channel.send('監視を開始します')

        const voiceChannel = msg.guild.channels.find('name', channelName)
        if (!voiceChannel || voiceChannel.type !== 'voice') {
            return msg.reply(`${channelName} というボイスチャンネルがみつかりません.`)
        }
        let db = getFirebaseDatabase()

        voiceChannel.join()
            .then(conn => {
                conn.on('speaking', (user, speaking) => {
                    let refVc = db.ref(`vc/${user.id}`);
                    var data = {}
                    data.voice = speaking
                    refVc.set(data)
                })
            }).catch(console.log);
    }
    
    if (msg.content === 'stop') {
        const voiceChannel = msg.guild.channels.find('name', channelName)
        voiceChannel.leave()
        msg.channel.send('監視を終了しました')
    }
});

client.login('XXXXXXXXXXXXXXXXXXXXXXXX')
```

待機チャンネルで「rec」と打ち込むと監視が始まり、「stop」で止まります。botは指定のボイスチャンネルの各入室者が喋ったり黙ったりしたタイミングで、各個人のvoice状況をtrue/falseで書き込みます。

### 読み込み側Webクライアント（Vue.js）

今回は遅延チェックが目的なので、特定のユーザーのステータスだけ監視します。

```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>動作確認</title>

    <script src="https://www.gstatic.com/firebasejs/5.7.0/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/5.7.0/firebase-database.js"></script>

    <script src="https://cdn.jsdelivr.net/npm/vue"></script>

    <style type="text/css">  
      div#app {
        color: white;
        text-align: center;
        line-height: 100px;
        font-weight: bold;
        font-size: 60px;
      }
    </style>  
  </head>

  <body>
    <div id="app" v-bind:style="{ background: color }">
      {{ message }}
    </div>

    <script>
    var config = {
      apiKey: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
      authDomain: "{project-id}.firebaseapp.com",
      projectId: "{project-id}",
      databaseURL: "https://{project-id}.firebaseio.com/",
      storageBucket: ""
    };
    firebase.initializeApp(config);
    var database = firebase.database();

    var app = new Vue({
      el: '#app',
      data: {
        message: 'standby..',
        color : 'black'
      },
      mounted() {
        var vcRef = firebase.database().ref('vc/{user_id}');
        vcRef.on('value', (snapshot) => {
            var data = snapshot.val()
            if(data.voice) {
              this.message = 'speech';
              this.color = 'red';
            } else {
              this.message = 'stop';
              this.color = 'blue';
            }
        });
      }
    });
    </script>
  </body>
</html>
```

## 結果

YouTubeに掲載したので、詳細をご覧ください。

https://youtu.be/DM3UJJSOG-8

discordアプリ側のステータス変化に比べて、ハッキリとコンソール画面やWebクライアント画面の変化が遅れているのが見てとれます。具体的に動画編集ソフトで音声波形が出てからの変化を見ると、100〜200ms程度の遅れがあるような印象です。確かに遅延を極力減らしたいようなもの（アクション系ゲームなど）では採用できない速度ですね。

以上です。メリークリスマス。