cancel
Showing results for 
Search instead for 
Did you mean: 

Could Race Conditions Occur in This SAP CAP Scenario?

fabi2295
Explorer
0 Kudos
318

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! 🚀

Accepted Solutions (1)

Accepted Solutions (1)

fabi2295
Explorer
0 Kudos

@Willem_Pardaens

Hi, are you okay ?

The test is that two users are making two different posts at the same time, but in different entities.

However, the entity of the first user is picking up the email of the second user because they are running simultaneously.

 

fabi2295
Explorer
0 Kudos

Update: Found the Root Cause – It Was cds.connect.to Causing the Race Condition After further investigation, I found the root cause of the race condition in my SAP CAP service. The issue was due to the use of await cds.connect.to to call an internal service within the same CAP application.

cds.on('served', async ()=> {
  const db = await cds.connect.to('db') // DANGER: will cause race condition !!!
  db.on('before', (req) => console.log(req.event, req.path))
})


Documentation:  https://cap.cloud.sap/docs/get-started/troubleshooting#don-t

fabi2295_0-1743804454528.png

 

Willem_Pardaens
Product and Topic Expert
Product and Topic Expert
There is no need to use 'cds.connect.to' in your service, you can just use 'this'.
fabi2295
Explorer
0 Kudos

@Willem_Pardaens 

Hello,

we replaced cds.connect.to with cds.services, and the race condition behavior stopped.

Answers (1)

Answers (1)

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.