Skip to content

Commit

Permalink
Fix token aware metadata fetching deadlock.
Browse files Browse the repository at this point in the history
The token aware host policy needs to fetch schema metadata so it can
hash row keys properly. Before the internal "control" conn was added,
metadata.go would set Query().RoutingKey([]byte{}) before it performed
the metadata queries so the metadata queries themselves didn't re-enter
the token aware host selection code (and deadlock). The control conn
still works most of the time, though, because the control conn performs
queries directly on a conn object, bypassing the token aware selection
stuff. That is, unless there is more than one page of metadata, since
iter.next.fetch() executes the query via the session, which re-enters
the token aware host selection code.

Fix by setting RoutingKey to []byte{} on the control conn's
queries. This is the same tactic the old code was using to avoid this
problem.
  • Loading branch information
Muir Manders committed Feb 17, 2016
1 parent 2a72f6c commit 5ed8c04
Show file tree
Hide file tree
Showing 2 changed files with 10 additions and 2 deletions.
10 changes: 9 additions & 1 deletion cassandra_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ func TestCAS(t *testing.T) {

if _, err := session.Query(`DELETE FROM cas_table WHERE title = ? and revid = ? IF last_modified = ?`,
title, revid, tenSecondsLater).ScanCAS(); !strings.HasPrefix(err.Error(), "gocql: not enough columns to scan into") {
t.Fatal("delete: was expecting count mismatch error but got: %q", err.Error())
t.Fatalf("delete: was expecting count mismatch error but got: %q", err.Error())
}

if applied, err := session.Query(`DELETE FROM cas_table WHERE title = ? and revid = ? IF last_modified = ?`,
Expand Down Expand Up @@ -1879,13 +1879,21 @@ func TestTokenAwareConnPool(t *testing.T) {
cluster := createCluster()
cluster.PoolConfig.HostSelectionPolicy = TokenAwareHostPolicy(RoundRobinHostPolicy())

// force metadata query to page
cluster.PageSize = 1

session := createSessionFromCluster(cluster, t)
defer session.Close()

if expected := cluster.NumConns * len(session.ring.allHosts()); session.pool.Size() != expected {
t.Errorf("Expected pool size %d but was %d", expected, session.pool.Size())
}

// add another cf so there are two pages when fetching table metadata from our keyspace
if err := createTable(session, "CREATE TABLE gocql_test.test_token_aware_other_cf (id int, data text, PRIMARY KEY (id))"); err != nil {
t.Fatalf("failed to create test_token_aware table with err: %v", err)
}

if err := createTable(session, "CREATE TABLE gocql_test.test_token_aware (id int, data text, PRIMARY KEY (id))"); err != nil {
t.Fatalf("failed to create test_token_aware table with err: %v", err)
}
Expand Down
2 changes: 1 addition & 1 deletion control.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ func (c *controlConn) withConn(fn func(*Conn) *Iter) *Iter {

// query will return nil if the connection is closed or nil
func (c *controlConn) query(statement string, values ...interface{}) (iter *Iter) {
q := c.session.Query(statement, values...).Consistency(One)
q := c.session.Query(statement, values...).Consistency(One).RoutingKey([]byte{})

for {
iter = c.withConn(func(conn *Conn) *Iter {
Expand Down

0 comments on commit 5ed8c04

Please sign in to comment.