Using startBackgroundTransaction in Node.js with Promises

I have so far been unable to use startBackgroundTransaction with the Node.js agent, using promises, and_not_ using getTransaction() nor transaction.end(). I would really like to see a functional example of how to do this, but have been unable to find one, despite that the documentation claims it is possible.

This NewRelic Documentation, under “Instrument Background Transactions,” claims that what I want to do is possible: “If the handler passed to startBackgroundTransaction returns a promise, the agent will end the transaction when the returned promise resolves or rejects.” I cannot get it to do this: the transactions succeed but NewRelic does not seem to receive those background transactions’ data.

I also found this section from the api docs. How crestfallen was I to discover it includes an example (yay!) and describes how to use promises (yay!) but does not include an example of how to use promises.

OK, here’s my code, what am I doing wrong? How do I get NewRelic to start a new background transaction upon startBackgroundTransaction() and get it to consider that the beginning the the transaction, and the resolution or rejection of the returned promise as the end?

From the typescript, here’s where the call is made:

class PucNewRelicImpl extends PucNewRelic {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  readonly _newrelic: any;
  constructor() {
    super();
    this._newrelic = require("newrelic");
  }

  async startNonWebTransactionInner(
    name: string,
    group = "ungrouped",
    callback: () => Promise<unknown>
  ): Promise<void> {
    this._newrelic.startBackgroundTransaction(name, group, callback);
  }

Notice that typescript’s type system does require here that the callback returns a promise. The typescript does compile, so we are guaranteed that the callback passed to startBackgroundTransaction does return a promise. Here is the invocation point:

First, a factory allowing us to swap-in a mock NewRelic agent:

static async startNonWebTransaction(
  name: string,
  group: string,
  callback: () => Promise<unknown>
 ): Promise<unknown> {
  return this.factory().startNonWebTransactionInner(name, group, callback);
}
static factory(): PucNewRelic {
  if (process.env.NEW_RELIC_LICENSE_KEY) {
    return new PucNewRelicImpl();
  } else {
    return new PucNewRelic();
  }
}

And finally, the true invocation point:

async logMonSetupBullJob(job: Job<WorkQueueOrderEvent>): Promise<void> {
  this.order_job_id = job.id;
  try {
    await WorkQueueJobContext.runAndReturn(async () => {
      await PucNewRelic.startNonWebTransaction(
        this.type,
        WorkQueue.workQueueOrderName,
        await this.logMonFinishSetupBullJob()
      );
    });
  } catch (e) {
    logger.error("500 error equivalent thrown in work queue", e);
  }
}
async logMonFinishSetupBullJob(): Promise<() => Promise<void>> {
  await this.logMonFinishSetupBullJobHook();
  logger.info(`Dequeued order event command`);
  return this.processBullJob();
}
processBullJob(): () => Promise<void> {
  throw new Error(
    "Implement in subclasses, then use factory methods from " +
      "workQueueOrderEventFactory.ts to produce objects capable of " +
      "receiving this message"
  );
}

Here’s an example subclass implementation of processBullJob():

processBullJob(): () => Promise<void> {
  return async () => {
    await this.validateAndCreate();
  };
}

Any help? The whole system is working, however, NewRelic does not seem to get any background transactions.

OK, this is one of those times where yup, it’s totally user error.

The problem is the location of the newrelic.js configuration file. It wasn’t being found by the work queue process, and so newrelic was not starting in that process. It turns out all the code above was just fine.

Feeling silly but maybe it will help someone.

2 Likes

This topic was automatically closed after 365 days. New replies are no longer allowed.