浏览代码

Fix token aware metadata fetching deadlock.

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.
Muir Manders 9 年之前
父节点
当前提交
5ed8c04148
共有 2 个文件被更改,包括 10 次插入2 次删除
  1. 9 1
      cassandra_test.go
  2. 1 1
      control.go

+ 9 - 1
cassandra_test.go

@@ -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 = ?`,
@@ -1879,6 +1879,9 @@ 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()
 
@@ -1886,6 +1889,11 @@ func TestTokenAwareConnPool(t *testing.T) {
 		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)
 	}

+ 1 - 1
control.go

@@ -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 {