Skip to content

Commit 2a5056d

Browse files
committed
Integration test for Metadata_changed mechanism
1 parent 31d10f9 commit 2a5056d

File tree

3 files changed

+107
-11
lines changed

3 files changed

+107
-11
lines changed

cassandra_test.go

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3397,8 +3397,6 @@ func TestLargeSizeQuery(t *testing.T) {
33973397
t.Fatal(err)
33983398
}
33993399

3400-
defer session.Close()
3401-
34023400
longString := strings.Repeat("a", 500_000)
34033401

34043402
err := session.Query("INSERT INTO gocql_test.large_size_query (id, text_col) VALUES (?, ?)", "1", longString).Exec()
@@ -3430,8 +3428,6 @@ func TestQueryCompressionNotWorthIt(t *testing.T) {
34303428
t.Fatal(err)
34313429
}
34323430

3433-
defer session.Close()
3434-
34353431
str := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()_+"
34363432
err := session.Query("INSERT INTO gocql_test.large_size_query (id, text_col) VALUES (?, ?)", "1", str).Exec()
34373433
if err != nil {
@@ -3446,3 +3442,101 @@ func TestQueryCompressionNotWorthIt(t *testing.T) {
34463442

34473443
require.Equal(t, str, result)
34483444
}
3445+
3446+
func TestPrepareExecuteMetadataChangedFlag(t *testing.T) {
3447+
// This test ensures that the whole Metadata_changed flow
3448+
// is handled properly.
3449+
//
3450+
// To trigger C* to return Metadata_changed we should do:
3451+
// 1. Create a table
3452+
// 2. Prepare stmt which uses the created table
3453+
// 3. Change the table schema in order to affect prepared stmt (e.g. add a column)
3454+
// 4. Execute prepared stmt. As a result C* should return RESULT/ROWS response with
3455+
// Metadata_changed flag, new metadata id and updated metadata resultset.
3456+
//
3457+
// The driver should handle this by updating its prepared statement inside the cache
3458+
// when it receives RESULT/ROWS with Metadata_changed flag
3459+
session := createSession(t)
3460+
defer session.Close()
3461+
3462+
if session.cfg.ProtoVersion < protoVersion5 {
3463+
t.Skip("Metadata_changed mechanism is only available in proto > 4")
3464+
}
3465+
3466+
if err := createTable(session, "CREATE TABLE IF NOT EXISTS gocql_test.metadata_changed(id int, PRIMARY KEY (id))"); err != nil {
3467+
t.Fatal(err)
3468+
}
3469+
3470+
err := session.Query("INSERT INTO gocql_test.metadata_changed (id) VALUES (?)", 1).Exec()
3471+
if err != nil {
3472+
t.Fatal(err)
3473+
}
3474+
3475+
// We have to specify conn for all queries to ensure that
3476+
// all queries are running on the same node
3477+
conn := session.getConn()
3478+
3479+
const selectStmt = "SELECT * FROM gocql_test.metadata_changed"
3480+
queryBeforeTableAltering := session.Query(selectStmt)
3481+
queryBeforeTableAltering.conn = conn
3482+
row := make(map[string]interface{})
3483+
err = queryBeforeTableAltering.MapScan(row)
3484+
if err != nil {
3485+
t.Fatal(err)
3486+
}
3487+
3488+
require.Len(t, row, 1, "Expected to retrieve a single column")
3489+
stmtCacheKey := session.stmtsLRU.keyFor(conn.host.HostID(), conn.currentKeyspace, queryBeforeTableAltering.stmt)
3490+
inflight, _ := session.stmtsLRU.get(stmtCacheKey)
3491+
preparedStatementBeforeTableAltering := inflight.preparedStatment
3492+
3493+
// Changing table schema in order to cause C* to return RESULT/ROWS Metadata_changed
3494+
alteringTableQuery := session.Query("ALTER TABLE gocql_test.metadata_changed ADD new_col int")
3495+
alteringTableQuery.conn = conn
3496+
err = alteringTableQuery.Exec()
3497+
if err != nil {
3498+
t.Fatal(err)
3499+
}
3500+
3501+
// Expecting C* will return RESULT/ROWS Metadata_changed
3502+
// and it will be properly handled
3503+
queryAfterTableAltering := session.Query(selectStmt)
3504+
queryAfterTableAltering.conn = conn
3505+
row = make(map[string]interface{})
3506+
err = queryAfterTableAltering.MapScan(row)
3507+
if err != nil {
3508+
t.Fatal(err)
3509+
}
3510+
3511+
// Ensuring if cache contains updated prepared statement
3512+
require.Len(t, row, 2, "Expected to retrieve both columns")
3513+
inflight, _ = session.stmtsLRU.get(stmtCacheKey)
3514+
preparedStatementAfterTableAltering := inflight.preparedStatment
3515+
require.NotEqual(t, preparedStatementBeforeTableAltering.resultMetadataID, preparedStatementAfterTableAltering.resultMetadataID)
3516+
require.NotEqual(t, preparedStatementBeforeTableAltering.response, preparedStatementAfterTableAltering.response)
3517+
3518+
// Executing prepared stmt and expecting that C* won't return
3519+
// Metadata_changed because the table is not being changed.
3520+
// Running query with timeout to ensure there is no deadlocks.
3521+
// However, it doesn't 100% proves that there is a deadlock...
3522+
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30000)
3523+
defer cancel()
3524+
3525+
queryAfterTableAltering2 := session.Query(selectStmt).WithContext(ctx)
3526+
queryAfterTableAltering2.conn = conn
3527+
row = make(map[string]interface{})
3528+
err = queryAfterTableAltering2.MapScan(row)
3529+
if err != nil {
3530+
if errors.Is(err, context.DeadlineExceeded) {
3531+
t.Fatal("It is likely failed due deadlock")
3532+
}
3533+
t.Fatal(err)
3534+
}
3535+
3536+
// Ensuring metadata of prepared stmt is not changed
3537+
require.Len(t, row, 2, "Expected to retrieve both columns")
3538+
inflight, _ = session.stmtsLRU.get(stmtCacheKey)
3539+
preparedStatementAfterTableAltering2 := inflight.preparedStatment
3540+
require.Equal(t, preparedStatementAfterTableAltering.resultMetadataID, preparedStatementAfterTableAltering2.resultMetadataID)
3541+
require.Equal(t, preparedStatementAfterTableAltering.response, preparedStatementAfterTableAltering2.response)
3542+
}

conn.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1613,8 +1613,6 @@ func (c *Conn) executeQuery(ctx context.Context, qry *Query) *Iter {
16131613
return &Iter{framer: framer}
16141614
case *resultRowsFrame:
16151615
if x.meta.newMetadataID != nil {
1616-
// Updating the result metadata id in prepared stmt
1617-
//
16181616
// If a RESULT/Rows message reports
16191617
// changed resultset metadata with the Metadata_changed flag, the reported new
16201618
// resultset metadata must be used in subsequent executions
@@ -1631,8 +1629,8 @@ func (c *Conn) executeQuery(ctx context.Context, qry *Query) *Iter {
16311629
},
16321630
}
16331631
c.session.stmtsLRU.add(stmtCacheKey, newInflight)
1634-
// Closing done here because the stmtsLRU already contains a new inflight
1635-
// with updated metadata and result metadata id
1632+
// The driver should close this done to avoid deadlocks of
1633+
// other subsequent requests
16361634
close(newInflight.done)
16371635
}
16381636
}
@@ -1643,7 +1641,7 @@ func (c *Conn) executeQuery(ctx context.Context, qry *Query) *Iter {
16431641
numRows: x.numRows,
16441642
}
16451643

1646-
if params.skipMeta {
1644+
if params.skipMeta && x.meta.noMetaData() {
16471645
if info != nil {
16481646
iter.meta = info.response
16491647
iter.meta.pagingState = copyBytes(x.meta.pagingState)

frame.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,8 +1031,12 @@ func (r *resultMetadata) morePages() bool {
10311031
return r.flags&flagHasMorePages == flagHasMorePages
10321032
}
10331033

1034+
func (r *resultMetadata) noMetaData() bool {
1035+
return r.flags&flagNoMetaData == flagNoMetaData
1036+
}
1037+
10341038
func (r resultMetadata) String() string {
1035-
return fmt.Sprintf("[metadata flags=0x%x paging_state=% X columns=%v]", r.flags, r.pagingState, r.columns)
1039+
return fmt.Sprintf("[metadata flags=0x%x paging_state=% X columns=%v new_metadata_id=% X]", r.flags, r.pagingState, r.columns, r.newMetadataID)
10361040
}
10371041

10381042
func (f *framer) readCol(col *ColumnInfo, meta *resultMetadata, globalSpec bool, keyspace, table string) {
@@ -1072,7 +1076,7 @@ func (f *framer) parseResultMetadata() resultMetadata {
10721076
meta.newMetadataID = copyBytes(f.readShortBytes())
10731077
}
10741078

1075-
if meta.flags&flagNoMetaData == flagNoMetaData {
1079+
if meta.noMetaData() {
10761080
return meta
10771081
}
10781082

0 commit comments

Comments
 (0)