async fetchEvents(mailgunOptions, batchHandler, {maxEvents = Infinity} = {}) {
        const mailgunInstance = this.getInstance();
        if (!mailgunInstance) {
            logging.warn(`Mailgun is not configured`);
            return;
        }

        debug(`[MailgunClient fetchEvents]: starting fetching first events page`);
        const mailgunConfig = this.#getConfig();
        const startDate = new Date();
        const overallStartTime = Date.now();

        let batchCount = 0;
        let totalBatchTime = 0;

        try {
            let page = await this.getEventsFromMailgun(mailgunInstance, mailgunConfig, mailgunOptions);

            // By limiting the processed events to ones created before this job started we cancel early ready for the next job run.
            // Avoids chance of events being missed in long job runs due to mailgun's eventual-consistency creating events outside of our 30min sliding re-check window
            let events = (page?.items?.map(this.normalizeEvent) || []).filter(e => !!e && e.timestamp <= startDate);
            debug(`[MailgunClient fetchEvents]: finished fetching first page with ${events.length} events`);

            let eventCount = 0;
            const beginTimestamp = mailgunOptions.begin ? Math.ceil(mailgunOptions.begin * 1000) : undefined; // ceil here if we have rounding errors

            while (events.length !== 0) {
                const batchStartTime = Date.now();
                await batchHandler(events);
                const batchEndTime = Date.now();
                const batchDuration = batchEndTime - batchStartTime;

                batchCount += 1;
                totalBatchTime += batchDuration;

                eventCount += events.length;

                if (eventCount >= maxEvents && (!beginTimestamp || !events[events.length - 1].timestamp || (events[events.length - 1].timestamp.getTime() > beginTimestamp))) {
                    break;
                }

                const nextPageId = page.pages.next.page;
                debug(`[MailgunClient fetchEvents]: starting fetching next page ${nextPageId}`);
                page = await this.getEventsFromMailgun(mailgunInstance, mailgunConfig, {
                    page: nextPageId,
                    ...mailgunOptions
                });

                // We need to cap events at the time we started fetching them (see comment above)
                events = (page?.items?.map(this.normalizeEvent) || []).filter(e => !!e && e.timestamp <= startDate);
                debug(`[MailgunClient fetchEvents]: finished fetching next page with ${events.length} events`);
            }

            const overallEndTime = Date.now();
            const totalDuration = overallEndTime - overallStartTime;
            const averageBatchTime = batchCount > 0 ? totalBatchTime / batchCount : 0;

            logging.info(`[MailgunClient fetchEvents]: Processed ${batchCount} batches in ${(totalDuration / 1000).toFixed(2)}s. Average batch time: ${(averageBatchTime / 1000).toFixed(2)}s`);
        } catch (error) {
            logging.error(error);
            throw error;
        }
    }
Fetches events from Mailgun
@param {Object} mailgunOptions
@param {Function} batchHandler
@param {Object} options
@param {Number} options.maxEvents Not a strict maximum. We stop fetching after we reached the maximum AND received at least one event after begin (not equal) to prevent deadlocks.
@returns {Promise<void>}