Weird behavior with Sets in App Services Function

Hi,
This is my first time using the Atlas App Services, and while working on my app I faced a weird issue with functions which I’m not able to explain & figure out. I’m sure there is an explanation for the behavior, it will be great if someone can explain it.

So here it goes:
The below code is not exactly my app code, but it is sufficient to explain the behavior. I have created a function myFunc which has 2 nested loops for rows & columns. For every row I get 2 column numbers by calling another function getSetWithNumbers in the same file. This function returns these 2 numbers in a Set.

const COLS = 6;
const ROWS = 6;

function getSetWithNumbers(start, end) {
  const set = new Set();

  const num1 = start;
  let num2 = start + 2;
  if (num2 > end) {
    num2 = start + 2 - end;
  } else if (num2 === end) {
    num2 = 0;
  }

  set.add(num1);
  set.add(num2);

  console.log(
    `Added ${num1} & ${num2} to the set for row ${start}, set size: ${set.size}`
  );

  return set;
}

function myFunc(request, response) {
  for (let row = 0; row < ROWS; row++) {
    const mySet = getSetWithNumbers(row, ROWS);

    for (let col = 0; col < COLS; col++) {
      if (mySet.has(col)) {
        console.log(`Row-${row}: mySet has col ${col}: ${mySet.has(col)}`);
      }
    }
  }

  return 'Hello World!';
}

exports = myFunc;

Now here is the weird part:
If I call myFunc using my local node setup I get following logs (everything is as per the expectation):

Added 0 & 2 to the set for row 0, set size: 2
Row-0: mySet has col 0: true
Row-0: mySet has col 2: true
Added 1 & 3 to the set for row 1, set size: 2
Row-1: mySet has col 1: true
Row-1: mySet has col 3: true
Added 2 & 4 to the set for row 2, set size: 2
Row-2: mySet has col 2: true
Row-2: mySet has col 4: true
Added 3 & 5 to the set for row 3, set size: 2
Row-3: mySet has col 3: true
Row-3: mySet has col 5: true
Added 4 & 0 to the set for row 4, set size: 2
Row-4: mySet has col 0: true
Row-4: mySet has col 4: true
Added 5 & 1 to the set for row 5, set size: 2
Row-5: mySet has col 1: true
Row-5: mySet has col 5: true

But when I call this function by pushing it to App Services I get the following logs:

Added 0 & 2 to the set for row 0, set size: 2 
Row-0: mySet has col 0: true 
Row-0: mySet has col 2: true 
Added 1 & 3 to the set for row 1, set size: 2 
Row-1: mySet has col 1: true 
Added 2 & 4 to the set for row 2, set size: 2 
Row-2: mySet has col 2: true 
Added 3 & 5 to the set for row 3, set size: 2 
Row-3: mySet has col 3: true 
Added 4 & 0 to the set for row 4, set size: 2 
Row-4: mySet has col 0: true 
Row-4: mySet has col 4: true 
Added 5 & 1 to the set for row 5, set size: 2 
Row-5: mySet has col 5: true

As you can see there are missing entries / logs. What is causing this behavior?

If I replace the Set with a List (with the corresponding changes for add → push & has → includes), everything works perfectly.

This is bugging me for last 3-4 days, so better to have an explanation :slight_smile:

Thanks

Hi @Rajeev_R_Sharma,

Thank you for raising this: we could reproduce the behaviour, and indeed it looks like Set.has(…) isn’t returning the expected results.

We’re opening an internal ticket about the matter, and will keep this post updated.

Thank you @Paolo_Manna.

I was thinking that may be there is some limitation with functions I’m not aware about. Good to know that I found a valid issue.

Hi @Rajeev_R_Sharma,

The Team responsible of the underlying function engine has confirmed that the error is due to mishandling of integer values. Set.has(…) should still work for other types, though.

(To clarify: the problem is that the addition of two integers is returning a float, that Set.has() doesn’t compare properly)

For the time being, you can use your workaround with List: we don’t have a timeline for the resolution yet, if there’s one soon we’ll update this post.

Thank you for your help in identifying this bug!

Thanks for the update @Paolo_Manna.

Glad that I could help in finding the bug. Came across it so shared with the right people :-). Now that the team has identified the root cause, it will be fixed in due course.

Just wanted to add one more observation with regards to the same. I think this issue happens only if the added integers are not consecutive (index wise). If you add numbers which are consecutive then it works fine. I tried with the following changes:

function getSetWithNumbersNew(start, end) {
  const set = new Set();
  let logContent = ['Added'];
  for (let i = start; i < end; i++) {
    set.add(i);
    logContent.push(i);
  }

  logContent.push(`to the set for row ${start}, set size: ${set.size}`);

  console.log(logContent.join(' '));

  return set;
}

And called this function instead (also changed ROWS & COLUMNS to 4 to reduce the number of logs), and I get the same result with my local node setup as well as the atlas function.

Added 0 1 2 3 to the set for row 0, set size: 4 
Row-0: mySet has col 0: true 
Row-0: mySet has col 1: true 
Row-0: mySet has col 2: true 
Row-0: mySet has col 3: true 
Added 1 2 3 to the set for row 1, set size: 3 
Row-1: mySet has col 1: true 
Row-1: mySet has col 2: true 
Row-1: mySet has col 3: true 
Added 2 3 to the set for row 2, set size: 2 
Row-2: mySet has col 2: true 
Row-2: mySet has col 3: true 
Added 3 to the set for row 3, set size: 1 
Row-3: mySet has col 3: true

That is all from my side.

Thanks

Indeed the problematic operation is the addition

not the insertion in the set, per se: if you don’t have an addition, you don’t get into the issue.

Oh! my bad. I didn’t read the sentence properly.

And that explains the actual issue which I faced (not the above dummy code)

const randomInt = (min, max) => {
  return Math.floor(Math.random() * (max - min)) + min;
};

In my actual code I was inserting random entries to the Set using the above function. So float is getting returned from that addition here as well. If I moved that outside min to inside Math.floor then probably it would have worked.

Thanks for explaining :pray: :slight_smile: