Reputation: 10673
When a user registers, my app does two things - it adds the user to the database and it sends a verification email:
...
err := collection("users").Insert(&u);
if err != nil {
WriteServerError(w, err)
return
}
if err = sendVerificationEmail(&u); err != nil {
WriteServerError(w, err)
}
...
I wouldn't want to add the user to the database if the email does not get sent nor would I want to send the email if the user didn't get added to the database (the latter is of course taken care of with the code blocks in this order).
Assuming Go supports transactions, is it worth bothering with for something like this? If so then could someone give me some pointers as to how I would convert this code above?
I could use nested statements but this could get very ugly.
Upvotes: 0
Views: 264
Reputation: 43
You can use database transaction for this.
Sample code:
package main
import (
"database/sql"
_ "github.com/lib/pq"
"log"
"net/smtp"
)
func main() {
db, err := sql.Open("postgres", "user=user dbname=postgres")
if err != nil {
log.Fatal(err)
return
}
defer db.Close()
tnx, err := db.Begin()
if err != nil {
log.Fatal(err)
return
}
user := "test"
password := "testpassword"
res, err := tnx.Exec("INSERT INTO users(login, password) VALUES (?, ?)", user, value)
if err != nil {
log.Fatal(err)
err := tnx.Rollback()
if err != log.Fatal(err) {
log.Fatal(err)
}
return
}
err := smtp.SendMail(
"smpt.google.com", smtp.Auth{}, "[email protected]",
[]string{"[email protected]"}, []byte("Hello"))
if err != nil {
log.Fatal(err)
err := tnx.Rollback()
if err != log.Fatal(err) {
log.Fatal(err)
}
return
}
err := tnx.Commit()
if err != nil {
log.Fatal(err)
return
}
}
Yes, you can catch deadlock or database connection lost when commit transaction, but this is so rare case, you don't worry about it. Or if you want 100% success guarantee, use temporary user creation scheme.
Upvotes: 0
Reputation: 383
It's best to just add the user to the database in an unverified state. You can clean them out a few weeks later if they never verify.
Upvotes: 0
Reputation: 222511
I think that first of all you have to ask yourself why can you get a database/email sending error. If your database is unreliable and 2% of the time the insert fails, then may be you have to chose some other database. If the problem is that database failed, then you have to worry about way more problems than just a couple of unsend emails.
If the database is reliable and the problem is with sending email because you use some 3rd party provider and sometimes the API fails, than you can use something like
for {
if err = sendVerificationEmail(&u); err != nil {
WriteServerError(w, err)
} else {
break
}
}
Another approach is to save information about the emails that should be saved in a database and use another process (which runs every X seconds) to check for all unsend emails and send them. Once the email is send, remove the entry from the table.
Upvotes: 1
Reputation: 9509
There is no "transaction" in Go. The better you can do is to take the steps in the right order:
That said, generally speaking, there is no need to go to step 4. The common approach to this problem is to have a "I didn't get the verification email, please send another." button somewhere along your registration/login pages. You can't even be sure that your email arrived even if you sent it correctly: it could be filtered by the anti-spam, deleted by a missclick, etc.
Upvotes: 1