Guarantee read-your-write with replicas
penberg opened this issue · comments
We have reports that the following scenario breaks read-your-write guarantee with replicas:
// CREATE TABLE companies (id INT PRIMARY KEY, score INT);
package main
import (
"context"
"database/sql"
"fmt"
_ "github.com/tursodatabase/libsql-client-go/libsql"
"log"
"math/rand"
"os"
)
func main() {
ctx := context.Background()
dbUrl := os.Getenv("LIBSQL_URL")
authToken := os.Getenv("LIBSQL_AUTH_TOKEN")
db, err := sql.Open("libsql", dbUrl+"?authToken="+authToken)
if err != nil {
log.Fatal(err)
}
err = db.Ping()
if err != nil {
log.Fatal(err)
}
for i := 0; i < 100; i++ {
fmt.Printf(".")
id := rand.Intn(10)
origScore := rand.Intn(100)
updateSql := "INSERT INTO companies (id, score) VALUES (?, ?) ON CONFLICT (id) DO UPDATE SET score = ?"
_, err = db.ExecContext(ctx, updateSql, id, origScore, origScore)
if err != nil {
log.Fatal(err)
}
var newScore int
if err = db.QueryRowContext(ctx, "SELECT score FROM companies WHERE id = ?", id).Scan(&newScore); err != nil {
if err == sql.ErrNoRows {
log.Fatal(fmt.Errorf("no such company"))
}
log.Fatal(err)
}
if origScore != newScore {
log.Fatal(fmt.Errorf("read your write violation"))
}
}
}
@MarinPostma pointed out that we need to request a read at the same replication index as the last write in the client to guarantee read-your-write. The replication index support in the protocol is undocumented, but here are some pointers:
https://github.com/tursodatabase/libsql/blob/main/libsql-server/src/hrana/proto.rs#L16-L35
https://github.com/tursodatabase/libsql/blob/main/libsql-server/src/hrana/proto.rs#L95
Depending on the Hrana API you are using, you may be missing the response replication index. It's there for batch response, but may be missing in other places, let me know, and I'll make it a priority to fix it for you