0

I use following code to accept the regsiter call from client

.post('/regsiter', async (ctx) => {
  requestfrom = JSON.parse(JSON.stringify(ctx.request.body))
  let regxemail = /^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/;
  let email = requestfrom.email
  let password = md5(requestfrom.password)
  if (regxemail.test(email)) {
    await userModel.checkemailexist([email])
        .then(async(result) => {
            if (result.length === 0) {
                console.log("insert email to database")
                await userModel.insertUser([email,password])
            } else {
                console.log("email exist")
            }
        })
      }
})

If the visiter's network is well, this function will work well, but if the visiter's network is so slow, there's no enough response time, and it will insert some same datas into database, the result.length is always ===0, how can I stop this, any ideas?

O. Jones
  • 103,626
  • 17
  • 118
  • 172
yupang
  • 155
  • 1
  • 2
  • 16
  • 1
    Is it possible your users are hitting Refresh when they get slow responses? That might account for mutiple requests. Also, your code sample does not show where you actually insert the the row. Please [edit] your question. – O. Jones Jul 26 '18 at 17:56
  • Oh, and you didn't ask about thiis but MD5 is an **insecure** way to hash your passwords. It's child's play to recover the passwords from MD5 hashes. If you're not sure why this is a problem, please see https://haveibeenpwned.com/ – O. Jones Jul 26 '18 at 17:58
  • @O.Jones question is edited, and of course I don't only use `MD5`, here my question is why stop multiple requests from one person when the network is so slow. – yupang Jul 26 '18 at 18:04
  • Slowness shouldn't cause duplicates. Your user mashing the button over and over again may though. What is the actual cause? Where is the code that contains the insert into the database? Is there a unique constraint on your table to prevent duplicates/could that be added? – JNevill Jul 26 '18 at 18:55
  • @JNevill Please look at my code above, the line `await userModel.insertUser([email,password])` – yupang Jul 26 '18 at 18:58

1 Answers1

1

You have a race condition. Specifically, if your node server receives a duplicate request while awaiting completion of your insertUser() method, that second rquest may find that the checkmailexist() method returns false. So both concurrent request processors will attempt to insert the same email value. This can happen whem your (impatient) user hits Refresh or clicks the Submit button again when the network is slow.

In other words, two concurrent requests for the same value of email may, sometimes, both yield false to your checkmailexist() function. Then they will both proceed to call insertUser() mentioning the same email. So you get a duplicate record.

Database systems provide serveral ways to handle this very common problem. One is to create a unique index on your table, so a second attempt to insert the same value will throw an error. Then your program can catch and ignore the duuplicate key error.

Database systems also provide transactions. You could begin the transaction when you query the database and commit it after you perform the insert, or roll it back if the email already exists. This will make the second invocation of checkemailexist() await the completion of the first check / insert sequence. It's difficult to tell you how to implement such database transactions without knowing what's in your methods.

MySQL offers INSERT ... IGNORE and INSERT ... ON DUPLICATE KEY UPDATE ... statements. These, combined with a unique key on your email column, let you handle the duplication logic in SQL.

Again, this is a common problem. All scalable database applications must deal with it.

O. Jones
  • 103,626
  • 17
  • 118
  • 172