Click here to Skip to main content
15,887,135 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I've built a small mobile app using React Native (Expo) with Firebase. The app allows users to view their portfolio, transactions, PNL, etc. However, I'm facing an issue where fetching data from the database is taking an excessive amount of time.
const Home = ({ navigation, route }) => {
  const userId = route.params?.userId;
  const [clientData, setClientData] = useState(null);
  const [latestAnnouncement, setLatestAnnouncement] = useState(null);

  useEffect(() => {
    if (!userId) {
      console.error("No user ID provided");
      return;
    }

    const fetchData = async () => {

    
      const startTimeClientData = Date.now();
      const clientRef = doc(FIRESTORE_DB, "clients", userId);
      const depositsRef = collection(clientRef, "deposits");
      const withdrawalsRef = collection(clientRef, "withdrawals");
      const dataPNLRef = query(collection(FIRESTORE_DB, "dataPNL"), orderBy("date", "desc"), limit(4));
    
      const [clientDoc, depositsSnapshot, withdrawalsSnapshot, dataPNLSnapshot] = await Promise.all([
        getDoc(clientRef),
        getDocs(depositsRef),
        getDocs(withdrawalsRef),
        getDocs(dataPNLRef),
      ]);
      console.log("Fetching client data:", Date.now() - startTimeClientData, "s");
    

      const dataPNL = dataPNLSnapshot.docs.map(doc => doc.data());
      const latestValue = dataPNL?.[0]?.value || 0;
      const initialDeposit = clientDoc.data().initialDeposit || 0;
      const totalDeposits = depositsSnapshot.docs.reduce((sum, doc) => sum + (doc.data().value || 0), 0);
      const totalWithdrawals = withdrawalsSnapshot.docs.reduce((sum, doc) => sum + (doc.data().value || 0), 0);
      let initialBalance = initialDeposit + totalDeposits - totalWithdrawals;
      if (totalDeposits === 0 && totalWithdrawals === 0) {
        initialBalance = initialDeposit;
      }
      const finalBalance = (Math.round(initialBalance * (1 + latestValue / 100) * 100) / 100).toFixed(2);
      const latestWithdrawal = withdrawalsSnapshot.docs[0]?.data()?.value || 0;
      const latestDeposit = depositsSnapshot.docs[0]?.data()?.value || 0;
      const monthlyPnL = dataPNL ? dataPNL.reduce((sum, data) => sum + (data?.value || 0), 0) : 0;

      setClientData({
        currentBalance: finalBalance,
        latestWithdrawal,
        latestDeposit,
        latestValue,
        monthlyPnL
      });
      const announcementsRef = collection(FIRESTORE_DB, "announcements");
      const latestAnnouncementRef = query(announcementsRef, orderBy("date", "desc"), limit(1));
      const latestAnnouncementSnapshot = await getDocs(latestAnnouncementRef);
    
      setLatestAnnouncement(latestAnnouncementSnapshot.docs[0]?.data());
    };

    fetchData();
  }, [userId]);

My observations:

The initial read takes around 7 seconds, while subsequent reads take 1-2 seconds

What I have tried:

Fetching them separately using multiple async functions takes even longer.
The time taken seems to be volatile and inconsistent.

Here's an example of the time logs:

LOG Fetching client data: 3030 ms
LOG Fetching deposits: 827 ms
LOG Fetching withdrawals: 2009 ms
LOG Fetching dataPNL: 1168 ms
LOG Fetching latest announcement: 158 ms
Fetching Client Data: The client data was retrieved from the collection containing information about individual clients.

Fetching Deposits: The deposits sub-collection, containing values and dates for each client, was fetched next.

Fetching Withdrawals: The withdrawal sub-collection was then fetched, containing the values and dates for each client's withdrawals.

Fetching DataPNL: The dataPNL collection, which includes weekly data on percentage and dates.
I have also tried adding indexing for the collections in the Firebase index option, but it didn't make a significant difference.
Posted
Updated 13-Aug-23 10:17am
v2
Comments
Richard MacCutchan 13-Aug-23 8:21am    
You have not mentioned how many records the app is processing.
Mohamad Shoumar 13-Aug-23 8:23am    
less than 100, the clients are 15 in total. Btw I just managed to reduce the fetching time by changing the order by to ascending instead of descending to align with fire store's default ascending order
const latestAnnouncementRef = query(announcementsRef, orderBy("date", "asc"), limit(1));
[no name] 13-Aug-23 11:08am    
Looks like you have "4 queries" ... which means 4 round-trips (?) The "server" should deal with a (single) request that can be optimized from it's end, before returning the results. (A "client query" is "one query" ... not a bunch strung together. The trap of "parallel processing".)
Mohamad Shoumar 13-Aug-23 14:09pm    
from what you said I understood that I should handle the logic on the server side and then call it from the client side, so I created a cloud function and then called it but it's still taking the same amount of time. However, I'm seeing that fetching the user's info and balance and stuff like that is rarely taking >1s. The main issue is with the announcement which is taking well above 1s
[no name] 13-Aug-23 15:16pm    
With almost any process, there's "startup time" that can't be eliminated; perhaps that what's you're dealing with (and why "batching" minimizes that); you just have to time every "step" to see where the bottleneck is (and if it can be "fixed"). "Controls" have a "loading" and "loaded" events that help with performance analysis.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900