-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
colrpc: fix tracing when an inbox receives an error
Currently, whenever an inbox encounters an error in `Next`, regardless of the origin of error, we close the inbox which results in shutting down the gRPC stream. However, such behavior is intended to occur only in case of an ungraceful termination of the gRPC stream itself, and for other error cases (like an error is sent from the outbox, internal error in the inbox) we want to keep the stream alive in order to be able to drain it later. As a consequence of the current incorrect behavior, whenever a remote node encounters an error, we will not collect the remote metadata, so we won't populate the trace (if the tracing is enabled). This bug was introduced during a large refactor in 3771c58 and is now fixed. Another thing this commit fixes is that all metadata objects that are sent together with an error by the outbox are now correctly buffered to be returned when draining. Previously, they would just get lost, but that didn't matter since the inbox was marked as "done", so draining was a noop (mistakenly). Release note (bug fix): Previously, whenever a distributed query resulted in an error on the remote node, then the trace would be incomplete. This is now fixed.
- Loading branch information
1 parent
a0632ce
commit 773d9ca
Showing
5 changed files
with
157 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
// Copyright 2022 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
package colflow_test | ||
|
||
import ( | ||
"context" | ||
"math" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/cockroachdb/cockroach/pkg/base" | ||
"github.com/cockroachdb/cockroach/pkg/settings/cluster" | ||
"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" | ||
"github.com/cockroachdb/cockroach/pkg/testutils/testcluster" | ||
"github.com/cockroachdb/cockroach/pkg/util/leaktest" | ||
"github.com/cockroachdb/cockroach/pkg/util/log" | ||
"github.com/cockroachdb/cockroach/pkg/util/mon" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
// TestDrainingAfterRemoteError verifies that the draining is fully completed if | ||
// an error occurs on a remote node. The verification is done by checking that | ||
// the trace from a distributed query contains spans of the remote processors. | ||
func TestDrainingAfterRemoteError(t *testing.T) { | ||
defer leaktest.AfterTest(t)() | ||
defer log.Scope(t).Close(t) | ||
|
||
ctx := context.Background() | ||
|
||
// Create a disk monitor for the temp storage only with 1 byte of space. | ||
// This ensures that the query will run into "out of temporary storage" | ||
// error. | ||
diskMonitor := mon.NewMonitor( | ||
"test-disk", | ||
mon.DiskResource, | ||
nil, /* curCount */ | ||
nil, /* maxHist */ | ||
-1, /* increment: use default block size */ | ||
math.MaxInt64, | ||
cluster.MakeTestingClusterSettings(), | ||
) | ||
diskMonitor.Start(ctx, nil /* pool */, mon.MakeStandaloneBudget(1)) | ||
|
||
// Set up a two node cluster. | ||
tempStorageConfig := base.TempStorageConfig{InMemory: true, Mon: diskMonitor} | ||
args := base.TestClusterArgs{ | ||
ServerArgs: base.TestServerArgs{TempStorageConfig: tempStorageConfig}, | ||
ReplicationMode: base.ReplicationManual, | ||
} | ||
tc := testcluster.StartTestCluster(t, 2 /* nodes */, args) | ||
defer tc.Stopper().Stop(ctx) | ||
|
||
// Create two tables, one with small values, and another with large rows. | ||
// Relocate the range for the small table to node 2. | ||
conn := tc.ServerConn(0) | ||
sqlDB := sqlutils.MakeSQLRunner(conn) | ||
sqlDB.Exec(t, "CREATE TABLE small (k INT PRIMARY KEY);") | ||
sqlDB.Exec(t, "INSERT INTO small SELECT generate_series(1, 100);") | ||
sqlDB.Exec(t, "ANALYZE small;") | ||
sqlDB.Exec(t, "ALTER TABLE small EXPERIMENTAL_RELOCATE VALUES (ARRAY[2], 2)") | ||
sqlDB.Exec(t, "SELECT count(*) FROM small;") | ||
sqlDB.Exec(t, "CREATE TABLE large (k INT PRIMARY KEY, v STRING);") | ||
sqlDB.Exec(t, "INSERT INTO large SELECT generate_series(1, 100), repeat('a', 100000);") | ||
sqlDB.Exec(t, "ANALYZE large;") | ||
|
||
// Make sure that the query is fully distributed (i.e. all execution happens | ||
// on node 2). | ||
sqlDB.Exec(t, "SET distsql = always;") | ||
|
||
// Sanity check that, indeed, node 2 is part of the physical plan. | ||
rows, err := conn.Query("EXPLAIN (VEC) SELECT sum(length(v)) FROM large, small WHERE small.k = large.k GROUP BY large.k;") | ||
require.NoError(t, err) | ||
defer func() { | ||
require.NoError(t, rows.Close()) | ||
}() | ||
var foundNode2 bool | ||
for rows.Next() { | ||
var line string | ||
require.NoError(t, rows.Scan(&line)) | ||
if strings.Contains(line, "Node 2") { | ||
foundNode2 = true | ||
} | ||
} | ||
require.True(t, foundNode2, "expected that most of the work is done on node 2") | ||
|
||
// Lower the workmem setting so that the join reader gets a memory error | ||
// first, followed by the temp disk storage error. | ||
sqlDB.Exec(t, "SET distsql_workmem = '8MiB';") | ||
// Enable the tracing since we'll check that it contains spans for the join | ||
// reader. | ||
sqlDB.Exec(t, "SET tracing = on;") | ||
|
||
// Perform a query that uses the join reader when ordering has to be | ||
// maintained. Ensure that it encounters the error that we expect. | ||
sqlDB.ExpectErr(t, ".*test-disk.*", "SELECT sum(length(v)) FROM large, small WHERE small.k = large.k GROUP BY large.k;") | ||
sqlDB.Exec(t, "SET tracing = off;") | ||
|
||
// Now, the crux of the test - verify that the spans for the join reader on | ||
// the remote node have been imported into the trace on the gateway. If we | ||
// see no such spans, then the draining wasn't fully performed. | ||
row := conn.QueryRow(`SELECT count(*) FROM [SHOW TRACE FOR SESSION] WHERE operation = 'join reader'`) | ||
var count int | ||
require.NoError(t, row.Scan(&count)) | ||
require.True(t, count > 0, "expected to find some spans for join reader in the trace") | ||
} |