


Firebase Realtime Database プレゼンス機能
Firebase Realtime Database には、接続しているユーザーがオフラインになった際に、特定のドキュメントに対して処理を行える仕組みがあります。
例えば、以下のような感じで、データベースとの切断時に、特定のドキュメントを削除することができます。
DatabaseReference presenceRef = FirebaseDatabase.getInstance().getReference("info");
presenceRef.setValue(ServerValue.TIMESTAMP);
presenceRef.onDisconnect().removeValue();
アプリが突然終了するといった不測の事態でも、ドキュメントに対して最後の処理を行わせることができるので便利です。
失敗したこと
この機能を使っていて、1つ失敗したことがありましたので、ご紹介したいと思います。
ユーザー情報を格納するドキュメントに onDisconnect().removeValue() の処理を実行し、ユーザーがオフラインになった場合には、必ずそのドキュメントが削除されることを期待した実装にしていました。
ところが、ある時、アプリを終了してもドキュメントが削除されず、残ってしまう事があることに気付きました。プレゼンス機能を使えば、どんなタイミングでアプリを落としても、必ずドキュメントが削除されるものと思っていたため、かなり困惑してしまいました。
原因と解決策
その原因ですが、Firebase Authentication からのログアウトと、セキュリティルールの記述内容によるものでした。
セキュリティルールではよく、本人のユーザデータのみ変更可能とするために、以下のようなルールを指定することがあります。
".write": "auth.uid == $userId"
この指定がされているドキュメントは、onDisconnect().removeValue() の指定がされていたとしても、Firebase Auth からのログアウトした状態では、ドキュメントが自動的に削除されることがなかったのです。onDisconnect() は呼ばれるものの、removeValue() に失敗するという形ですね。
解決策ですが、真っ当な方法としては、Firebase Auth からログアウトする前に、ドキュメントを削除する処理を実行するということですね。
または、以下の処理を実行することで、強制的に対象のドキュメントの onDisconnect() を呼び出すことができました。
FirebaseDatabase.getInstance().goOffline();
この処理によってデータベースから切断されますので、再びデータベースを使用する場合は、goOnline() を呼び出して接続する必要があります。