Home > database >  NodeJS insert voucher code to first person who calls API
NodeJS insert voucher code to first person who calls API

Time:11-23

I don't know if this has a solution already but I can't find it or I don't know what to search.

I have a rest api which returns a list of products and I want to add a voucher code to the response of the first person who calls the api. I'm using redis to cache the information of the user who received the code, that expires within 15 mins.

async function addVoucherCode(response, userId) {
    try {
    const key = "KEY_VOUCHER_CODE";

    let cachedData = await redis.get(key);
    if (cachedData) {
        if (cachedData.userId === userId) response.voucherCode = cachedData.voucherCode;

        return;
    }

    const voucherCode = await createVoucherCode(userId); //call to create voucher code and save to db
    if (!voucherCode) return;

    await redis.setEx(key, 15 * 60, {userId, voucherCode});
    response.voucherCode = cachedData.voucherCode;

    } catch (err) {
       console.error("[Error] addVoucherCode: ", err);
    }
}

I created a function that mimics a simultaneous request, and when I checked the response, all them have a voucher code, not just the first.

async function getProducts(url, params) {
try {
    const customers = [
        { id: 1, token: "Bearer eyJhbGciOi....1" },
        { id: 2, token: "Bearer eyJhbGciOi....2"}, 
        { id: 3, token: "Bearer eyJhbGciOi....3"}, 
        { id: 4, token: "Bearer eyJhbGciOi....4"}
    ];

    const data = await Promise.all(customers.map( async customer  => {
        return await fetch(url   "?"   params.toString(), {
            headers: {
                Authorization: customer.token
            },
        }).then(res => res.json());
    }));

    data.forEach((item, indx) => {
       if(item.voucherCode) {
          const id = customers[indx].id;
          console.log(`Customer ${id} has a voucher!!!!!!!!!!!!!`)
       }
    })
} catch (err) {
    console.error("[Error] getProducts: ", err);
}
}

Result

Customer 1 has a voucher!!!!!!!!!!!!!
Customer 2 has a voucher!!!!!!!!!!!!!
Customer 3 has a voucher!!!!!!!!!!!!!
Customer 4 has a voucher!!!!!!!!!!!!!

I tried adding a 200ms delay inside addVoucherCode but same result. Thanks in advance for the help.

CodePudding user response:

You are calling addVoucherCode in a sync loop, so it'll run 4 times in parallel (and the 4 GET commands will be issued at the same time, it'll reply with null to all of them, and all of them will call createVoucherCode).

There are 2 things you can do to fix it:

  1. Cache the promise of createVoucherCode:
const createVoucherCodePromises = new Map();
function createVoucherCode(userId) {
  if (!createVoucherCodePromises.has(userId)) {
    createVoucherCodePromises.set(
      userId,
      _createVoucherCode(userId)
        .finally(() => createVoucherCodePromises.delete(userId))
    );
  }

  return createVoucherCodePromises.get(userId);
}

async function _createVoucherCode(userId) {
 // ...
}

NOTE: this will not solve the problem if you have multiple node processes running at the same time.

  1. Use SET with NX (won't override existing values) and GET (return existing/old value)
> SET key voucher1 NX GET
OK
> SET key voucher2 NX GET # will return the existing value without overriding it
"voucher1"
> GET key
"voucher1"
  • Related