-
Notifications
You must be signed in to change notification settings - Fork 623
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ExecuteBatchCAS not applied
, MapExecuteBatchCAS is applied
#1746
Comments
@danthegoodman1 Hello, I am investigating this issue. For now, I can verify that When using Also, If the data state matches the condition (like |
@testisnullus I'm not sure you've understood the issue, or rather I was likely not clear enough. The issue is that |
@danthegoodman1 I am testing on the Cassandra 4.1.4 version. The
I've called the
If I println the Could you please provide more information about your issues with the |
No errors, I noticed purely that it would never apply (checking the DB shows that it indeed did not apply), but if I simply swapped it with the |
Here is the keyspace and table I used func createKeyspace(s *gocql.Session) error {
if err := s.Query("CREATE KEYSPACE if not exists test WITH replication = {'class': 'NetworkTopologyStrategy', 'replication_factor' : 1}").Exec(); err != nil {
return fmt.Errorf("error creating keyspace: %w", err)
}
return nil
}
func createTable(s *gocql.Session) error {
if err := s.Query("create table if not exists testkv (key text, col text, ts bigint, val blob, primary key (key, col, ts)) with clustering order by (col desc, ts desc)").Exec(); err != nil {
return fmt.Errorf("error creating table: %w", err)
}
return nil
} |
@danthegoodman1 Hello, I've tested the The table looks like this after batch operation:
I can share the full code snippet with you also:
The |
Just tested on the latest ScyllaDB image (scylladb/scylla:latest), and it also works well without any issues. What version of GoCQL and Scylla do you use now? |
Scylla is still latest, gocql was version |
I was also quite confused considering I changed nothing on the query and simply swapped out |
@danthegoodman1 I cannot reproduce the issue for now :( |
I had just swapped out for the map one with no issues.
…On Mon, May 6, 2024 at 4:20 AM Danylo Savchenko ***@***.***> wrote:
@danthegoodman1 <https://github.com/danthegoodman1> I cannot reproduce
the issue for now :(
Does the issue affect executing queries for you or can you easily use the
MapExecuteBatchCAS() function for your operations? I will be tracking
this issue and will try to test the ExecuteBatchCAS() more closely to
identify this problem's root cause.
—
Reply to this email directly, view it on GitHub
<#1746 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AEH3JLEDM2EDBGFGBXZCTK3ZA44MHAVCNFSM6AAAAABF3LN6KCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAOJVGQZTEOJSGI>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Interestingly, I'm also having the same issue happen to me. What's even more strange is that even though |
Hello. I guess I successfully reproduced it. I used the code provided by @testisnullus and adjusted it a bit to run on Scylla 6.0. I'm experiencing getting
Also, I can't reproduce it on the latest Cassandra. The driver behaves the way it should and returns My Scylla setup: docker run --name scylla-latest --hostname some-scylla -p 9042:9042 -d scylladb/scylla --smp 1 My Cassandra setup: docker run --name cassandra-latest -p 9043:9042 -d cassandra The code I used to reproduce it: package main
import (
"fmt"
"log"
"time"
"github.com/gocql/gocql"
)
type Tx struct {
session *gocql.Session
pendingWrites map[string][]byte
readTime time.Time
table string
}
type TxnAborted struct{}
func (e *TxnAborted) Error() string {
return "Transaction aborted due to write conflict"
}
func createKeyspace(s *gocql.Session) error {
//if err := s.Query("CREATE KEYSPACE if not exists test WITH replication = {'class': 'NetworkTopologyStrategy', 'replication_factor' : 1}").Exec(); err != nil {
// return fmt.Errorf("error creating keyspace: %w", err)
//}
// Scylla 6.0 specific code. LWT is not yet supported with tablets
if err := s.Query("CREATE KEYSPACE if not exists test WITH replication = {'class': 'NetworkTopologyStrategy', 'replication_factor' : 1} AND TABLETS = {'enabled': false}").Exec(); err != nil {
return fmt.Errorf("error creating keyspace: %w", err)
}
return nil
}
func createTable(s *gocql.Session) error {
// Table here won't be created because the keyspace is not specified...
//if err := s.Query("create table if not exists testkv (key text, col text, ts bigint, val blob, primary key (key, col, ts)) with clustering order by (col desc, ts desc)").Exec(); err != nil {
// return fmt.Errorf("error creating table: %w", err)
//}
if err := s.Query("create table if not exists test.testkv (key text, col text, ts bigint, val blob, primary key (key, col, ts)) with clustering order by (col desc, ts desc)").Exec(); err != nil {
return fmt.Errorf("error creating table: %w", err)
}
return nil
}
func (tx *Tx) ExecuteBatchTransaction() error {
b := tx.session.NewBatch(gocql.UnloggedBatch)
for key, val := range tx.pendingWrites {
b.Entries = append(b.Entries, gocql.BatchEntry{
//Stmt: fmt.Sprintf("insert into \"%s\" (key, ts, col, val) values (?, 0, 'l', ?) if not exists", tx.table),
// DO NOT USE "\"\ for table name
Stmt: fmt.Sprintf("insert into %s (key, ts, col, val) values (?, 0, 'l', ?) if not exists", tx.table),
Args: []any{key, []byte("encodedLock")},
})
// Insert the data record
b.Entries = append(b.Entries, gocql.BatchEntry{
//Stmt: fmt.Sprintf("insert into \"%s\" (key, ts, col, val) values (?, ?, 'd', ?) if not exists", tx.table),
Stmt: fmt.Sprintf("insert into %s (key, ts, col, val) values (?, ?, 'd', ?) if not exists", tx.table),
Args: []any{key, tx.readTime.UnixNano(), val},
})
}
//m := make(map[string]any)
applied, _, err := tx.session.ExecuteBatchCAS(b)
if err != nil {
return fmt.Errorf("error in ExecuteBatchCAS: %w", err)
}
fmt.Println("APPLIED?", applied)
//fmt.Println("MapExecuteBatchCAS:", m)
if !applied {
return fmt.Errorf("%w: prewrite not applied (conflict)", &TxnAborted{})
}
fmt.Println(applied)
return nil
}
func main() {
// connect to the cluster
cluster := gocql.NewCluster("localhost:9042") // scylla
//cluster := gocql.NewCluster("localhost:9043") // cassandra
cluster.Consistency = gocql.Quorum
cluster.ProtoVersion = 4
cluster.ConnectTimeout = time.Second * 10
// Keyspace is being created when session is already initialized...
//cluster.Keyspace = "test"
session, err := cluster.CreateSession()
if err != nil {
log.Println(err)
return
}
defer session.Close()
// keyspace "test" is being created here
err = createKeyspace(session)
if err != nil {
fmt.Println("Keyspace failed:", err)
}
// table "testkv" in ks "test"
err = createTable(session)
if err != nil {
fmt.Println("Table failed:", err)
}
tx := Tx{
session: session,
pendingWrites: map[string][]byte{"key1": []byte("value1")},
readTime: time.Now(),
// It won't work because keyspace is not specified...
// Transaction failed: error in ExecuteBatchCAS: No keyspace has been specified. USE a keyspace, or explicitly specify keyspace.tablename
//table: "testkv",
table: "test.testkv",
}
err = tx.ExecuteBatchTransaction()
if err != nil {
fmt.Println("Transaction failed:", err)
}
} I'm not a Scylla expert, so I can't say if this is the driver issue or not. However, at least there is a way to reproduce it. |
Ok, now I see the difference. Cassandra when LWT is applied returns a single column If we adjust the code above then the driver will properly handle the var (
keyRead string
tsRead int64
colRead string
valRead string
)
applied, _, err := tx.session.ExecuteBatchCAS(b, &keyRead, &colRead, &tsRead, &valRead) @joao-r-reis I think this is a good idea to make func (s *Session) ExecuteBatchCAS(batch *Batch, dest ...interface{}) (applied bool, iter *Iter, err error) {
iter = s.executeBatch(batch)
if err := iter.checkErrAndNotFound(); err != nil {
iter.Close()
return false, nil, err
}
if len(iter.Columns()) > 1 {
dest = append([]interface{}{&applied}, dest...)
iter.Scan(dest...)
} else {
iter.Scan(&applied)
}
return applied, iter, nil // here is nil returned, but should be iter.err
} |
Yeah that's a good idea, we should create a JIRA for this |
And here it is: CASSGO-47 |
So basically you just have to add the columns to scan against? That’d be a good comment to add, however I'm sure that'd be the error then |
Yep, the current gocql codebase properly handles it when additional columns for scanning purpose are presented as I showed in the comment above, but is only true for Scylla, with Cassandra it works as it should without any additional columns because it returns only an |
oh i just edited without getting the update, I assume with the open PR it'd throw an error about not having enough columns so you'd know if that was the issue (it was being masked by the nil return) |
Yes, exactly |
Using the latest version of scylla on docker, I have the following code:
When using
ExecuteBatchCAS(b)
, theapplied
return would always befalse
, but when usingMapExecuteBatchCAS(b, make(map[string]interface{}))
(despite not actaully binding to anything),applied
would betrue
.Why is this? My understand is that it si a difference of binding only.
The text was updated successfully, but these errors were encountered: