Flutter で Firestore を使っていると unavailable になる問題
Flutter のパッケージ cloud_firestore を使っていると、不意に下記のエラーが出る。
The service is currently unavailable. This is a most likely a transient condition and may be corrected by retrying with a backoff.
- firebase_core: 0.5.1
- cloud_firestore: 0.14.2
これまでに(だいたい)分かっていること
- GitHub 上で複数同様の問題が観測されている
- iOS 側では問題が起きていないので、おそらく Android SDK に起因する問題(または cloud_firestore とネイティブSDKのインテグレーション)
- エラーメッセージには Exponential Backoff アルゴリズムを使ってリトライせよ、という示唆があるが、しても改善しない
- Firebase Authentication のトークンが expire されるタイミングでおきている疑いもあるが、強制リフレッシュをかけても解決しない
- 1ヶ月以上にわたり発生しているので、Firestore の障害ではなさそう
- 起きたり起きなかったりする
パッケージのアップグレードがでているので、こちらを調査してみる
- cloud_firestore: 0.14.4
- firebase_core: 0.5.3
で確認中。ただし変更差分を見ると、直接的に問題解決に寄与するコミットはなさそう
(Wi-Fi 接続に関する Issue だけど)この投稿によると
Kotlin で実装したときは問題ないので FlutterFire の問題ではないか、とのこと...。
- cloud_firestore: 0.14.4
にアップしたバージョンがリリースされているので、様子見。
にアップしたバージョンがリリースされているので、様子見。
改善しませんでした。
お疲れ様です!!
弊社のアプリ(react native)でも同様のcrash がおき、一点こう対応しましたという、報告になります。
const commonConfigDoc = () =>
firestore()
.collection(`config`)
.doc(`common`)
firestore との通信で、get() するときに、error handling をしていないことが原因でした・・・
このメソッドを使うときに、今までは
const { latestAppVersion } = await FirebaseService.getCommonConfig()
と書いて lastApVersion を 使い回していたのですが、
const { latestAppVersion } = await FirebaseService.getCommonConfig().catch(e => ...)
if(!latestAppVersion) {
...
}
というコードに変更しました!
しばらく、crash の動向を追って参ります!
どういうわけか12月24日頃から再発しなくなったのですが、どうですか?
ごめんなさい、返信が遅れました!!!
弊社もこのエラーがかなり発生率が下がってます!(先日のエラーハンドリングし忘れもあるかもですが)
引き続きwatch していきます!!
コメントありがとうございます!
現在運用中のコードを簡略化したものですが、リトライ処理を行ったうえでもなお例外が発生しているので、リトライ処理では復帰できない何かが起きている、というところまでは分かっています。
Future<DocumentSnapshot> getDocument({
String path,
String id,
}) async {
try {
final ref = await withRetrying<DocumentSnapshot>(
() {
return FirebaseFirestore.instance.collection(path).doc(id).get();
},
);
return ref;
} catch (e) {
// リトライ処理でも上手くいかなかった場合、ここに到達
rethrow;
}
}
withRetrying
の中身はざっくりこんな感じです。
import 'package:retry/retry.dart';
const firestoreRetryOptions = const RetryOptions(maxAttempts: 6);
Future<T> withRetrying<T>(Future<T> Function() fn) async {
final ref = await firestoreRetryOptions.retry(() async {
final _ref = await fn();
return _ref;
}, retryIf: (e) async {
if (e is FirebaseException &&
e.code == firestoreErrorCodePermissionDenined) {
return false;
}
return true;
}
return ref;
}
12月21日を境に発生しなくなっている件(おもだった関連アップデートは特になし)
問題が再開したので re-open
さすがに公式サポート事案では、と思ったのでお問い合わせ。現在も発生中。
さすがに公式サポート事案では、と思ったのでお問い合わせ。現在も発生中。
公式からの回答を意訳すると、Flutter の Firebase SDK は公式サポートではないのでコミュニティサポートを受けて下さい、とのことでした。
これ結局何が問題なんでしょう?Firestoreのサーバー側かクライアント側なのか...
エミュレーターでは通るんですけどね。
半年以上経ちましたが未解明ですね。
一部改善・解決したと報告しているユーザーからは、
shrinkResources false
minifyEnabled false
とすると良い、という話もでています。
ありがとうございます!それ設定してもすぐには解決しなかったんですけど、時間を空けたら治っていました。
でもこの問題が発生しているときはPlay StoreでのアプリのアップデートやYouTube MusicなどGoogle製アプリの送受信がとても遅くなっているような気がします。
他のサードパーティアプリはいつも通りです。
リージョンはどちらにしてますか?
僕は普段基本的にデフォルトのMulti-regionの nam5 (us-central)
にしてて、この問題が気になったことなかったです。
Cloud Firestore のロケーション | Firebase Documentation
ただ、Regionalのasia-northeast1
のプロジェクトでこの問題が起こって、リージョンの影響もありそうと推測しました。
Multi-regionがRegionalよりも可用性高いのは妥当ですし。
僕は普段基本的にデフォルトのMulti-regionの
nam5 (us-central)
にしてて
ちなみに、これは以下の理由です:
- これでも大抵の用途で遅くない
- Multi-regionの方が可用性・耐久性・SLAなど優れている
- 課金額はRegionalより1.5-2倍くらい高くなるが許容範囲
- 国外対応なども考慮すると
asia-northeast1
にするのも微妙
コメントありがとうございます! 僕の環境の リージョンは asia-northeast1
ですね。
そして マルチリージョンの採用なるほどです...!!! 確かに asia-northeast1
にするより us-central
のほうが良いと思いました。
僕の環境の リージョンは asia-northeast1 ですね。
ありがとうございます。
なるほど、マルチリージョン選ぶことで回避できる(あるいは発生頻度を大きく下げられる)可能性あるかもしれないですね🧐