2

I am trying to delete entire user records from firestore using cloud functions but encounters the next error

INVALID_ARGUMENT: maximum 500 writes allowed per request

How to workaround this?

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

exports.deleteUserContacts = functions
    .runWith({
        timeoutSeconds: 540,
        memory: '2GB'
    })
    .https.onCall((data,context) => {

    // ...

    return admin.firestore().collection('contacts').where('uid','==',context.auth.uid).get()
        .then(snap => {

            if (snap.size === 0) {
                console.log(`User ${context.auth.uid} has no contacts to delete`);
                return 'user has no contacts to delete';
            }

            let batch = admin.firestore().batch();

            snap.forEach(doc => {
                batch.delete(doc.ref)
            });

            return batch.commit(); //INVALID_ARGUMENT: maximum 500 writes allowed per request
        })
        .then(() => {
            console.log(`Transaction success on user ${context.auth.uid}`);
            return 'Transaction success';
        })
        .catch(error => {

            console.log(`Transaction failure on user ${context.auth.uid}`,error);
            throw new functions.https.HttpsError(
                'unknown',
                'Transaction failure'
              );
        });
});
epic
  • 1,333
  • 1
  • 13
  • 27

2 Answers2

3

To summarize final solution according to Stefan as worked for me.

If any error, please comment

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

exports.deleteUserContacts = functions
    .runWith({
        timeoutSeconds: 540,
        memory: '2GB'
    })
    .https.onCall((data,context) => {

   //...

    return admin.firestore().collection('contacts').where('uid','==',context.auth.uid).get()
        .then(snap => {

            if (snap.size === 0) {
                console.log(`User ${context.auth.uid} has no contacts to delete`);
                return 'user has no contacts to delete';
            }

            const batchArray = [admin.firestore().batch()];
            let operationCounter = 0;
            let batchIndex = 0;

            snap.forEach(doc => {
                batchArray[batchIndex].delete(doc.ref);
                operationCounter++;

                if (operationCounter === 499) {
                    batchArray.push(admin.firestore().batch());
                    batchIndex++;
                    operationCounter = 0;
                }
            });

            batchArray.forEach(
                async batch => await batch.commit()
            );

            return 'function ended';
        })
        .then(() => {
            console.log(`Transaction success on user ${context.auth.uid}`);
            return 'Transaction success';
        })
        .catch(error => {

            console.log(`Transaction failure on user ${context.auth.uid}`,error);
            throw new functions.https.HttpsError(
                'unknown',
                'Transaction failure'
              );
        });
});
epic
  • 1,333
  • 1
  • 13
  • 27
1

As you might know this is due to the limit to transactions and batched writes, in order to circumvent that limitation I found this SO post of another person that had a similar issue to you.

Most of the recommendations are batching it under 500 and then commiting.

Here, have a look at the solution here.

Hope this helps.

Stefan G.
  • 890
  • 5
  • 10