Skip to content

@firebase/firestore: Firestore (10.7.1): Could not reach Cloud Firestore backend. Backend didn't respond within 10 seconds. #3482

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
kshkrao3 opened this issue Dec 21, 2023 · 6 comments

Comments

@kshkrao3
Copy link

kshkrao3 commented Dec 21, 2023

Issue detail

I'm getting the below error almost whenever any user opens my site.

@firebase/firestore: Firestore (10.7.1): Could not reach Cloud Firestore backend. Backend didn't respond within 10 seconds.
This typically indicates that your device does not have a healthy Internet connection at the moment. The client will operate in offline mode until it is able to successfully connect to the backend.

I reached out to Firebase support, and this is what they had to say.

Just to let you know, AngularFire is no part of our official SDK's, and therefore is outside the scope of our support. Our support scope is limited for troubleshooting specific technical issues while implementing Firebase official APIs and features.

Given these circumstances, I can proportionate the following solutions:
Double check your computer for any extensions or software that could be interfering with the connection to Firestore
Ensure the project's tools and Firebase libraries including the firebase-tools package, are up to date to prevent compatibility issues and unexpected behaviors
Try to enable "experimentalForceLongPolling". You can take as a reference the following github issues that are similar to the reported issue: example 1, example 2
File an issue directly on the AngularFire's Github

I tried to follow their instructions by adding experimentalforcelongpolling in my environment file but that did not help.

I could not find any documentation on how to use this with the firestore for the way I'm initializing the app in my app.module.ts.

My app.module.ts looks like this:

292240350-0e99c9d1-a216-446d-a240-89fbb3f97b3b

Is there any way I can try to add experimentalforcelongpolling by the way I'm initializing the app? or is there anything else I can try to resolve this issue?

The site URL to reproduce the issue: https://rangapayana.com

Version info

Angular: 16.2.12

Firebase:^10.7.0

AngularFire:^16.0.0

Expected behavior

The application should load the data normally.

Actual behavior

The site will be loaded blank with the above-mentioned error.

Please let me know if you need any more information. I'd be happy to add.

@google-oss-bot
Copy link

This issue does not seem to follow the issue template. Make sure you provide all the required information.

@Sapython
Copy link

Very little information to deduce the problem. Please upload a part of your project on stackblitz where we can reproduce this issue. Or if possible please share a github repository.

@kshkrao3
Copy link
Author

@Sapython - The initialization part is very generic which anyone would do normally. It's been shared in the description. Apart from this, I have a lazyLoad service file that would be invoked with the required parameters every time on different components.

Please find the contents of the file below.

//// This Service would be a non singleton instance since it is used by multiple list modules and data has to be fresh for each module
//// Hence, its not provided through Service module, but rather its provided through individual list components
import { Injectable, OnDestroy } from "@angular/core";
import {
  AngularFirestore,
  DocumentSnapshot,
  Query,
} from "@angular/fire/compat/firestore";
import { WhereFilterOp } from "@firebase/firestore-types";
import { Subject } from "rxjs";
import { takeUntil, auditTime, map } from "rxjs/operators";
import * as firebase from "firebase/compat/app";
import { NgxSpinnerService } from "ngx-spinner";
@Injectable({
  providedIn: "root",
})
export class LazyloadService<T> implements OnDestroy {
  tableData: T[] = [];
  where: { key: string; op: WhereFilterOp; value: string | string[] }[] | null =
    [];
  limit = 10;
  collection = "";
  dataLoading = false;
  doneLoading = false;
  spinnerName = "pageLoader";
  // save last document in snapshot of items received
  private lastInResponse: DocumentSnapshot<T> | any;

  // keep the array of first document of previous pages
  private prev_strt_at: DocumentSnapshot<T>[] = [];

  subs$: Subject<void> = new Subject<void>();

  constructor(
    private afs: AngularFirestore,
    private spinnerService: NgxSpinnerService
  ) {}

  loadItems({ isReset = false, isNext = false }) {
    if (isReset) {
      this.dataLoading = false;
      this.doneLoading = false;
      this.tableData = [];
      this.spinnerName = "pageLoader";
    }
    if (this.doneLoading) {
      this.spinnerService.hide("pageLoader");
      this.spinnerService.hide("contentLoader");
    }
    if (this.dataLoading || this.doneLoading) {
      return;
    }
    this.spinnerService.show(this.spinnerName);
    this.dataLoading = true;
    return this.afs
      .collection(this.collection, (ref) => this.createQueryRef(ref, isNext))
      .snapshotChanges()
      .pipe(
        takeUntil(this.subs$),
        auditTime(1000),
        map((response) => {
          this.doneLoading = response.length === 0;
          this.spinnerService.hide(this.spinnerName);
          this.lastInResponse = response[response?.length - 1]?.payload.doc;
          this.push_prev_startAt(this.lastInResponse);
          response.forEach((record) => {
            // Avoid pushing duplicates
            if (
              this.tableData.filter(
                (item: any) => item["id"] === record?.payload.doc.id
              ).length <= 0
            ) {
              this.tableData.push({
                ...(record?.payload.doc.data() as T),
                id: record?.payload.doc.id,
              });
            }
          });
          this.dataLoading = false;
          return;
        })
      );
  }

  private createQueryRef(ref: any, isNext: boolean): Query {
    let queryRef: Query = ref;
    let orderByKey: any = "modifiedDate";
    let order: any = "desc";
    if (this.where) {
      this.where.forEach((condition, index) => {
        const { orderByKeyVal, orderVal, queryRefVal } = this.updateQueryRef(
          condition,
          index,
          orderByKey,
          order,
          queryRef
        );
        queryRef = queryRefVal;
        orderByKey = orderByKeyVal;
        order = orderVal;
      });
    }
    queryRef = queryRef.limit(this.limit);
    if (orderByKey !== "date") {
      queryRef = queryRef.orderBy(orderByKey, order);
    }
    if (isNext && this.lastInResponse) {
      queryRef = queryRef.startAfter(this.lastInResponse);
    }
    return queryRef;
  }

  private updateQueryRef(
    condition: { key: string; op: WhereFilterOp; value: string | string[] },
    index: number,
    orderByKey: any,
    order: any,
    queryRef: Query
  ): { orderByKeyVal: any; orderVal: any; queryRefVal: Query } {
    const key =
      condition.key === "id"
        ? firebase.default.firestore.FieldPath.documentId()
        : condition.key;
    if (index === 0 && !condition.key?.includes(".")) {
      orderByKey = key;
      order = condition.key === "id" ? "asc" : "desc";
    } else if (condition.key?.includes(".")) {
      orderByKey = firebase.default.firestore.FieldPath.documentId();
    }
    if (condition.value instanceof Array) {
      condition.value = condition.value.splice(0, 10);
    }
    queryRef = queryRef.where(key, condition?.op, condition?.value);
    return {
      orderByKeyVal: orderByKey,
      orderVal: order,
      queryRefVal: queryRef,
    };
  }

  push_prev_startAt(prev_first_doc: DocumentSnapshot<T>) {
    this.prev_strt_at.push(prev_first_doc);
  }

  ngOnDestroy() {
    this.subs$.next();
    this.subs$.complete();
  }
}

Unfortunately, I would not be able to create a stack blitz project because of the complexity and unable to share access to the project because of the confidentiality.

@Sapython
Copy link

Ok, nothing here seems, that can cause connection issue.

I have a suggestions

  1. Comment all the modules of your app in app.module.ts
  2. Turn it into a blank app by commenting every route in app.routing
  3. Comment every imported module created by you in app.module.ts
  4. Comment every module of Firebase except provideFirestore and initializeApp in app.module.ts
  5. Then call a simple getDoc request inside the app.component.ts

Check if Firebase is working.

  • If yes then the issue has to do with this service or the project itself
  • but if it fails then it means Firebase is the issue.

@kshkrao3
Copy link
Author

Finally, after much digging around the internet, I've resolved this issue by the below steps. However, I was not able to find the root cause for the same.

My application uses a combination of modular v9 library and compatibility library for a few functionalities. This is where I had to use the initialization features of the application for both modular and compatibility libraries. I presume this could have been one of the reasons for the issue. Based on this presumption, I eliminated the dependency on the compatibility libraries and started using modular v9 features.

My code now looks like the below:

app.module.ts

import { APP_ID, NgModule } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { BrowserModule } from "@angular/platform-browser";
import { RouterModule } from "@angular/router";
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";

import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
import { SharedModule } from "./shared/shared.module";
import { CoreModule } from "./core/core.module";
// Firebase/Firestore
import { getApp, initializeApp, provideFirebaseApp } from "@angular/fire/app";
import { provideAuth, getAuth, connectAuthEmulator } from "@angular/fire/auth";
import {
  initializeAppCheck,
  provideAppCheck,
  ReCaptchaEnterpriseProvider,
} from "@angular/fire/app-check";
import {
  initializeFirestore,
  persistentLocalCache,
  persistentMultipleTabManager,
  provideFirestore,
  connectFirestoreEmulator,
  Firestore,
  getFirestore,
} from "@angular/fire/firestore";
import { environment } from "src/environments/environment";
import { NgxSpinnerModule } from "ngx-spinner";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import {
  getAnalytics,
  provideAnalytics,
  ScreenTrackingService,
  UserTrackingService,
} from "@angular/fire/analytics";
import { getPerformance, providePerformance } from "@angular/fire/performance";
import { LocationStrategy, PathLocationStrategy } from "@angular/common";
import { FullCalendarModule } from "@fullcalendar/angular"; // must go before plugins

declare global {
  // eslint-disable-next-line no-var
  var FIREBASE_APPCHECK_DEBUG_TOKEN: boolean | string | undefined;
}
if (!environment.production) {
  self.FIREBASE_APPCHECK_DEBUG_TOKEN = environment.appCheckDebug; // set to <debugToken> that registered with App-Check in Firebase Console
}
@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserAnimationsModule,
    BrowserModule,
    NgbModule,
    FormsModule,
    RouterModule,
    AppRoutingModule,
    SharedModule,
    CoreModule,
    NgxSpinnerModule,
    FullCalendarModule,
    provideFirebaseApp(() => initializeApp(environment.firebaseConfig)),
    provideFirestore(() => {
      let firestore: Firestore;
      if (!environment.production) {
        firestore = getFirestore(getApp());
        connectFirestoreEmulator(firestore, "localhost", 8089);
      } else {
        firestore = initializeFirestore(getApp(), {
          localCache: persistentLocalCache({
            tabManager: persistentMultipleTabManager(),
          }),
          experimentalAutoDetectLongPolling: true,
          experimentalLongPollingOptions: {
            timeoutSeconds: 30,
          },
        });
      }
      return firestore;
    }),
    provideAuth(() => {
      const auth = getAuth();
      if (!environment.production) {
        connectAuthEmulator(auth, "http://localhost:9099", {
          disableWarnings: true,
        });
      }
      return auth;
    }),
    providePerformance(() => getPerformance(getApp())),
    provideAppCheck(() =>
      initializeAppCheck(getApp(), {
        provider: new ReCaptchaEnterpriseProvider(environment.recaptchaSiteKey),
        isTokenAutoRefreshEnabled: true,
      })
    ),
    provideAnalytics(() => getAnalytics(getApp())),
  ],
  providers: [
    ScreenTrackingService,
    UserTrackingService,
    { provide: LocationStrategy, useClass: PathLocationStrategy },
    {
      provide: APP_ID,
      useValue: "serverApp",
    },
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}


lazyload.service.ts


//// This Service would be a non singleton instance since it is used by multiple list modules and data has to be fresh for each module
//// Hence, its not provided through Service module, but rather its provided through individual list components
import { Injectable, OnDestroy, inject } from "@angular/core";
import { WhereFilterOp } from "@firebase/firestore-types";
import { Subject } from "rxjs";
import { takeUntil, map } from "rxjs/operators";
import { NgxSpinnerService } from "ngx-spinner";
import {
  collection,
  query,
  Query,
  where,
  documentId,
  orderBy,
  limit,
  startAfter,
  DocumentSnapshot,
  CollectionReference,
  collectionSnapshots,
  Firestore,
} from "@angular/fire/firestore";
import { traceUntilFirst } from "@angular/fire/performance";
@Injectable({
  providedIn: "any",
})
export class LazyloadService<T> implements OnDestroy {
  tableData: T[] = [];
  where: { key: string; op: WhereFilterOp; value: string | string[] }[] | null =
    [];
  limit = 10;
  collection = "";
  dataLoading = false;
  doneLoading = false;
  spinnerName = "pageLoader";
  // save last document in snapshot of items received
  private lastInResponse: DocumentSnapshot<T> | any;
  // keep the array of first document of previous pages
  private prev_strt_at: DocumentSnapshot<T>[] = [];
  subs$: Subject<void> = new Subject<void>();
  private firestore: Firestore = inject(Firestore);
  private spinnerService: NgxSpinnerService = inject(NgxSpinnerService);

  loadItems({ isReset = false, isNext = false }) {
    if (isReset) {
      this.dataLoading = false;
      this.doneLoading = false;
      this.tableData = [];
      this.spinnerName = "pageLoader";
    }
    if (this.doneLoading) {
      this.spinnerService.hide("pageLoader");
      this.spinnerService.hide("contentLoader");
    }
    if (this.dataLoading || this.doneLoading) {
      return;
    }
    this.spinnerService.show(this.spinnerName);
    this.dataLoading = true;
    const collectionRef = collection(this.firestore, this.collection);
    const q = this.createQueryRef(collectionRef, isNext);
    return collectionSnapshots(q).pipe(
      traceUntilFirst(this.collection),
      takeUntil(this.subs$),
      map((response) => {
        this.doneLoading = response.length === 0;
        this.spinnerService.hide(this.spinnerName);
        this.lastInResponse = response[response?.length - 1];
        this.push_prev_startAt(this.lastInResponse);
        response.forEach((record) => {
          // Avoid pushing duplicates
          if (
            this.tableData.filter((item: any) => item["id"] === record?.id)
              .length <= 0
          ) {
            this.tableData.push({
              ...(record?.data() as T),
              id: record?.id,
            });
          }
        });
        this.dataLoading = false;
        return;
      })
    );
  }

  private createQueryRef(ref: CollectionReference, isNext: boolean): Query {
    let queryRef: Query;
    let orderByKey: any = "modifiedDate";
    let order: any = "desc";
    const conditions: any = [];
    if (this.where) {
      this.where.forEach((condition, index) => {
        const key = condition.key === "id" ? documentId() : condition.key;
        if (index === 0 && !condition.key?.includes(".")) {
          orderByKey = key;
          order = condition.key === "id" ? "asc" : "desc";
        } else if (condition.key?.includes(".")) {
          orderByKey = documentId();
        }
        if (condition.value instanceof Array) {
          condition.value = condition.value.splice(0, 10);
        }
        conditions.push(where(key, condition?.op, condition?.value));
      });
    }
    conditions.push(limit(this.limit));
    if (orderByKey !== "date") {
      conditions.push(orderBy(orderByKey, order));
    }
    if (isNext && this.lastInResponse) {
      conditions.push(startAfter(this.lastInResponse));
    }
    queryRef = query(ref, ...conditions);
    return queryRef;
  }

  push_prev_startAt(prev_first_doc: DocumentSnapshot<T>) {
    this.prev_strt_at.push(prev_first_doc);
  }

  ngOnDestroy() {
    this.subs$.next();
    this.subs$.complete();
  }
}


However, even after the above changes, I was getting the same behavior, which when further inspected, I noticed many of them were using @angular/[email protected] and I was using @angular/[email protected]. I went ahead and downgraded to the v7.x version, which is when I started getting a different issue before I could validate the actual issue.

ERROR FirebaseError: Expected type 'Query', but it was: a custom dh object

One of the suggestions was to downgrade the Firebase version as well to v9, which I followed. This, however, ended up resolving the issue but created one more issue which was related to the version of rxfire library used by angular/[email protected].

Ultimately, I had to override the rxfire version for angular/fire like below and I was successfully able to resolve the actual issue I had.

"overrides": {
    "@angular/fire@^7.6.1": {
      "rxfire@^6.0.0": "6.0.3"
    }
  }

I'm still not sure if this was a problem with @angular/[email protected]. I did not dare to test this with @angular/[email protected] as I had lost many days digging around resolving the issue.

Hope this helps someone.

@tmk1991
Copy link

tmk1991 commented Feb 20, 2025

@kshkrao3 - I'm getting similar errors when trying to move to 16+.

Expected type 'dh', but it was: a custom DocumentReference...

I'm not sure why either... Any update from your end?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants