Firebase Realtime DBのコネクションが永久に復活しない問題対応

## 概要

本記事は「[Angular アドベントカレンダー 2022](https://qiita.com/advent-calendar/2022/angular)」の14日目の記事です。
最近、開発中に出会った事象についてシェアします。

結構、Firebaseに関する内容ですが、Angularfireを利用していること、FirebaseもAngularもGoogle系の技術であることを踏まえて許してもらえればと思います。


## 事象

Firebase Realtime DBでコネクションを貼ったままバックグラウンドに移行させ数分間放置すると、接続が途切れて二度と復活しなくなりました。一度、サイトを更新すると再度接続するようになります。
参考までに、本事象を踏んだ実装としては以下のコードのような感じです。

```typescript
import { AngularFireDatabase } from "@angular/fire/database";

constructor(private afDB: AngularFireDatabase) {}

inAppNotificationSubscriber(roomId: number) {
    this.fetchNotificationArray(roomId)
        .subscribe((data: FirebaseNotification) => {
    });
}

fetchNotificationArray(roomId: number): Observable<FirebaseNotification> {
    return this.afDB
        .list("notifications/" + roomId, ref => {
            return ref
                .orderByChild("created_at")
        })
        .snapshotChanges()
        .pipe(
            map(talks => talks.map(talk => new FirebaseNotification(talk)))
        );
}

```

### 発生環境

本事象が発生したときの端末、バージョンはこちらです。他のバージョンでは検証できていませんが、おそらく同じ事象が発生するかと。

- 端末：スマホ（iPhone、Android含む）
- Ver
    - Angular: 6.1.0
    - Firebase: 7.14.3
    - angular/fire: 5.4.2
    - Ionic; 3.6.0

## 原因

根本的な原因はわかってないです。なので以下は予想です。
Firebaseの接続を貼ったままバックグラウンド状態で放置することで事象が発生するので、おそらくFirebaseのコネクションを明示的に切断しないまま、ブラウザアプリ（Safari、Chrome等）をバックグラウンドに持っていくことで、Firebaseのコネクションを残ってるけど、通信は切断されてる状態になるのがだめっぽいです。何かしら状態残りになってる様子です。

## 対応

### Firebaseの切断、復旧処理

Ionic Forumに似たような問題のスレッドがありました。[当該投稿](https://forum.ionicframework.com/t/firebase-connection-is-lost-and-never-come-back/43810/9)([https://forum.ionicframework.com/t/firebase-connection-is-lost-and-never-come-back/43810/9](https://forum.ionicframework.com/t/firebase-connection-is-lost-and-never-come-back/43810/9))の記事よりFirebaseの切断、復旧をちゃんと行うことで永久に切断されることを防ぐことが可能とのことでした。

コードとして書かれているのは以下です。

```javascript
document.addEventListener("resume", function () {
	Firebase.goOnline();
},false);  
		
document.addEventListener("pause", function () {
	Firebase.goOffline();
},false);
```

このgoOnline、goOfflineの呼び出しは、はAngularFireにも提供されており、以下のコードで実行可能です。

```typescript
import { AngularFireDatabase } from "@angular/fire/database";
constructor(private afDB: AngularFireDatabase) {}

goOffLine() {
    this.afDB.database.goOffline();
}

goOnLine() {
  this.afDB.database.goOnline();
}
```

goOnline、goOfflineについてFirebaseのドキュメントはこちら（`https://firebase.google.com/docs/reference/node/firebase.database.Database#gooffline`）

本事象も、バックグラウンドに行くタイミングで明示的にFirebaseとの接続をオフラインモードに変更することで、発生しなくなることがわかりました。

### Web、アプリそれぞれで切断、復旧のハンドリング

goOffline、goOnlineで問題を回避することができるので、あとはバックグラウンド、フォアグラウンドに行くタイミングでこの関数を発火させれば良いです。
Ionicを利用している場合、Platformというpause(バックグラウンド)、resume(フォアグラウンド)をハンドリングするためのメソッドを提供してくれるサービスがあるので、それを利用します。
コードにすると以下のような感じです。

```typescript
import { Platform } from '@ionic/angular';
import { AngularFireDatabase } from "@angular/fire/database";


@Component({...})
export class MyPage {
  constructor(public platform: Platform, private afDB: AngularFireDatabase) {
    this.platform.pause.subscribe(async () => {
      this.afDB.database.goOffline();
    });
    
    this.platform.resume.subscribe(async () => {
      this.afDB.database.goOnline();
    });
  }
}

```

Platformのresume/pauseは、Cordova/Capacitorを使っているIonicアプリなら問題ないが、通常のWebサイトの場合は動作しません。
Webサイトの場合は、[visibilitychange](https://developer.mozilla.org/ja/docs/Web/API/Document/visibilitychange_event)を利用することで、バックグラウンド、フォアグラウンドのハンドリングが可能となりました。

```typescript
  setVisiblityState() {
    document.addEventListener("visibilitychange", () => {
      if (document.visibilityState === "visible") {
          this.afDB.goOnLine();
      }

      if (document.visibilityState === "hidden") {
          this.afDB.goOffLine();
      }
    });
  }
```

## まとめ
以上で最近、開発中に出会ったFirebase Realtime DBのコネクションが永久に復活しない問題対応の内容になります。本当は、Firebase、AngularFireのバージョンを上げて検証したりしたかったのですが、ちょっと手がまわり切りませんでした。
ただ、本記事が同じ事象を踏んだ誰かの役に立つと幸いです。

それでは。