cancel
Showing results for 
Search instead for 
Did you mean: 

Could Race Conditions Occur in This SAP CAP Scenario?

fabi2295
Explorer
0 Kudos
335

I'm investigating a potential race condition in an SAP CAP service where req.user or cds.context might be overwritten by another request during an internal POST call.

Scenario:

  • The TesteC event posts data to the TesteA entity using a service connection to the same CAP application (cds.connect.to('TesteRace')).
  • The before CREATE handlers for TesteA and TesteB log and modify the req.user information before the record is created.
  • When TesteC is triggered multiple times concurrently (also posting to TesteB), could req.user or cds.context.user be overwritten during the before CREATE phase?

Here is a simplified version of the code:

const cds = require('@sap/cds');
const LOG = cds.log();

module.exports = class TesteRace extends cds.ApplicationService {
  init() {
    this.on('TesteC', async (req) => {
      try {
        const srv = await cds.connect.to('TesteRace');

        LOG.info(`Executing TesteC - User from req.user: ${req.user?.attr?.email || req?.user?.id || 'empty'}`);
        LOG.info(`Executing TesteC - User from cds.context: ${cds.context.user.attr.email || cds.context?.user?.id || 'empty'}`);

        return await srv.post(this.entities.TesteA).entries([{ nome: req?.data?.nome || Math.random() * 1000 }]);
      } catch (error) {
        req.error(error);
      }
    });

    this.before('CREATE', this.entities.TesteA, async (req) => {
      req.data.nome = `${req.user?.attr?.email || cds.context?.user?.id || cds.context?.user?.attr?.email} ${Math.random() * 1000}`;
      LOG.info(`Before CREATE TesteA - User from req.user: ${req.user.attr.email || 'empty'}`);
      LOG.info(`Before CREATE TesteA - User from cds.context: ${cds.context?.user?.id || cds.context?.user?.attr?.email || 'empty'}`);
    });

    this.before('CREATE', this.entities.TesteB, async (req) => {
      req.data.nome = `${req.user?.attr?.email || cds.context?.user?.id || cds.context?.user?.attr?.email} ${Math.random() * 1000}`;
      LOG.info(`Before CREATE TesteB - User from req.user: ${req.user.attr.email || 'empty'}`);
      LOG.info(`Before CREATE TesteB - User from cds.context: ${cds.context?.user?.id || cds.context?.user?.attr?.email || 'empty'}`);
    });

    return super.init();
  }
}

Issue:

  • In the SAP BTP trial environment, I could not reproduce a race condition.
  • However, in the customer’s environment, req.user seems to be overwritten when the internal POST call occurs.
  • If I remove the internal POST call, the issue disappears.
  • The scenario is hard to reproduce, even in the customer’s environment—out of 4,000 records created, only 17 were affected by this condition.
  • The email from one request is being overwritten by another request from a different user, which suggests that cds.context might not be properly isolated between requests.

Has anyone experienced a similar issue? Could cds.context unexpectedly be shared between requests? Any insights would be greatly appreciated! 🚀

View Entire Topic
rayyavu
Explorer
0 Kudos

Hello @fabi2295 ,

Please verify if SAP CAP's locking mechanisms can help prevent req.user and cds.context from being overwritten by concurrent requests. 

Please refer: https://cap.cloud.sap/docs/node.js/cds-ql#forupdate 

fabi2295
Explorer
0 Kudos
Hello @rayyavu, thank you for responding. From what I understand, this mechanism you sent me is for concurrent access to the table. In my case, the req is updated by the CAP and the cds.context.