Skip to content

Commit d3a6aaf

Browse files
Add more examples (#91)
* Add more examples * Apply suggestions from code review Co-authored-by: Copilot <[email protected]> * fix path? --------- Co-authored-by: Copilot <[email protected]>
1 parent 25af490 commit d3a6aaf

18 files changed

+1524
-11
lines changed
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
using ClickHouse.Driver.ADO;
2+
using ClickHouse.Driver.Utility;
3+
4+
namespace ClickHouse.Driver.Examples;
5+
6+
/// <summary>
7+
/// Demonstrates how to use Query IDs to track and monitor query execution.
8+
/// Query IDs are useful for:
9+
/// - Debugging and troubleshooting specific queries
10+
/// - Tracking query execution in system.query_log
11+
/// - Correlating client-side operations with server-side logs
12+
/// - Monitoring query progress and performance
13+
/// </summary>
14+
public static class QueryIdUsage
15+
{
16+
public static async Task Run()
17+
{
18+
using var connection = new ClickHouseConnection("Host=localhost");
19+
await connection.OpenAsync();
20+
21+
Console.WriteLine("Query ID Usage Examples\n");
22+
23+
// Example 1: Automatic Query ID
24+
Console.WriteLine("1. Automatic Query ID assignment:");
25+
await Example1_AutomaticQueryId(connection);
26+
27+
// Example 2: Custom Query ID
28+
Console.WriteLine("\n2. Setting a custom Query ID:");
29+
await Example2_CustomQueryId(connection);
30+
31+
// Example 3: Tracking query execution
32+
Console.WriteLine("\n3. Tracking query execution in system.query_log:");
33+
await Example3_TrackingQueryExecution(connection);
34+
35+
// Example 4: Cancelling a query by Query ID
36+
Console.WriteLine("\n4. Query cancellation using Query ID:");
37+
await Example4_QueryCancellation(connection);
38+
39+
Console.WriteLine("\nAll Query ID examples completed!");
40+
}
41+
42+
private static async Task Example1_AutomaticQueryId(ClickHouseConnection connection)
43+
{
44+
// When you don't set a QueryId, ClickHouse automatically generates one
45+
using var command = connection.CreateCommand();
46+
command.CommandText = "SELECT 'Hello from ClickHouse' AS message";
47+
48+
Console.WriteLine($" QueryId before execution: {command.QueryId ?? "(null)"}");
49+
50+
var result = await command.ExecuteScalarAsync();
51+
52+
// After execution, the QueryId is populated from the response headers
53+
Console.WriteLine($" QueryId after execution: {command.QueryId}");
54+
Console.WriteLine($" Result: {result}");
55+
}
56+
57+
private static async Task Example2_CustomQueryId(ClickHouseConnection connection)
58+
{
59+
// You can set your own Query ID before executing a query
60+
// This is useful for correlation with your application logs
61+
var customQueryId = $"example-{Guid.NewGuid()}";
62+
63+
using var command = connection.CreateCommand();
64+
command.CommandText = "SELECT version()";
65+
command.QueryId = customQueryId;
66+
67+
Console.WriteLine($" Custom QueryId: {customQueryId}");
68+
69+
var version = await command.ExecuteScalarAsync();
70+
Console.WriteLine($" ClickHouse version: {version}");
71+
Console.WriteLine($" QueryId remained: {command.QueryId}");
72+
}
73+
74+
private static async Task Example3_TrackingQueryExecution(ClickHouseConnection connection)
75+
{
76+
// Execute a query with a custom Query ID
77+
var trackableQueryId = $"trackable-{Guid.NewGuid()}";
78+
using (var command = connection.CreateCommand())
79+
{
80+
command.CommandText = $"SELECT 1";
81+
command.QueryId = trackableQueryId;
82+
await command.ExecuteNonQueryAsync();
83+
}
84+
85+
Console.WriteLine($" Executed query with ID: {trackableQueryId}");
86+
87+
// Wait a moment for the query to be logged
88+
await Task.Delay(1000);
89+
90+
// Query system.query_log to get information about our query
91+
// Note: system.query_log may need to be enabled in your ClickHouse configuration
92+
using (var command = connection.CreateCommand())
93+
{
94+
command.CommandText = @"
95+
SELECT
96+
query_id,
97+
type,
98+
query_duration_ms,
99+
read_rows,
100+
written_rows,
101+
memory_usage
102+
FROM system.query_log
103+
WHERE query_id = {queryId:String}
104+
AND type = 'QueryFinish'
105+
ORDER BY event_time DESC
106+
LIMIT 1
107+
";
108+
command.AddParameter("queryId", trackableQueryId);
109+
110+
try
111+
{
112+
using var reader = await command.ExecuteReaderAsync();
113+
if (reader.Read())
114+
{
115+
Console.WriteLine(" Query execution details from system.query_log:");
116+
Console.WriteLine($" Query ID: {reader.GetString(0)}");
117+
Console.WriteLine($" Type: {reader.GetString(1)}");
118+
Console.WriteLine($" Duration: {reader.GetFieldValue<ulong>(2)} ms");
119+
Console.WriteLine($" Rows read: {reader.GetFieldValue<ulong>(3)}");
120+
Console.WriteLine($" Rows written: {reader.GetFieldValue<ulong>(4)}");
121+
Console.WriteLine($" Memory usage: {reader.GetFieldValue<ulong>(5)} bytes");
122+
}
123+
else
124+
{
125+
Console.WriteLine(" (Query not yet in system.query_log - this table may have a delay or be disabled)");
126+
}
127+
}
128+
catch (ClickHouseServerException ex) when (ex.ErrorCode == 60)
129+
{
130+
Console.WriteLine(" (system.query_log table not available on this server)");
131+
}
132+
}
133+
}
134+
135+
private static async Task Example4_QueryCancellation(ClickHouseConnection connection)
136+
{
137+
// Demonstrate cancelling a long-running query using Query ID
138+
var cancellableQueryId = $"cancellable-{Guid.NewGuid()}";
139+
140+
Console.WriteLine($" Query ID: {cancellableQueryId}");
141+
Console.WriteLine(" Starting a long-running query (SELECT sleep(5))...");
142+
143+
// Start the long-running query in a background task
144+
var queryTask = Task.Run(async () =>
145+
{
146+
try
147+
{
148+
using var command = connection.CreateCommand();
149+
command.CommandText = "SELECT sleep(3)";
150+
command.QueryId = cancellableQueryId;
151+
152+
await command.ExecuteScalarAsync();
153+
Console.WriteLine(" Query completed (should have been cancelled)");
154+
}
155+
catch (ClickHouseServerException ex)
156+
{
157+
// Query was killed on the server
158+
Console.WriteLine($" Server error: {ex.Message}");
159+
}
160+
catch (Exception ex)
161+
{
162+
Console.WriteLine($" Query failed: {ex.Message}");
163+
}
164+
}, CancellationToken.None);
165+
166+
// Wait a bit for the query to start and be present in the log
167+
await Task.Delay(1000);
168+
169+
// Cancel using KILL QUERY from another connection. Note that closing a connection will NOT kill any running queries opened by that connection.
170+
Console.WriteLine($" Cancelling query using KILL QUERY...");
171+
try
172+
{
173+
// Create a separate connection for cancellation
174+
using var cancelConnection = new ClickHouseConnection("Host=localhost");
175+
await cancelConnection.OpenAsync();
176+
177+
using var cancelCommand = cancelConnection.CreateCommand();
178+
cancelCommand.CommandText = $"KILL QUERY WHERE query_id = '{cancellableQueryId}'";
179+
await cancelCommand.ExecuteNonQueryAsync();
180+
181+
Console.WriteLine(" KILL QUERY command sent");
182+
}
183+
catch (Exception ex)
184+
{
185+
Console.WriteLine($" Note: KILL QUERY failed (may require permissions): {ex.Message}");
186+
}
187+
188+
// Wait for the query task to complete
189+
await queryTask;
190+
}
191+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
using ClickHouse.Driver.ADO;
2+
using ClickHouse.Driver.Utility;
3+
4+
namespace ClickHouse.Driver.Examples;
5+
6+
/// <summary>
7+
/// Demonstrates how to use Session IDs for maintaining state across multiple queries.
8+
/// Sessions are primarily used for:
9+
/// - Creating and using temporary tables
10+
/// - Maintaining query context across multiple statements
11+
///
12+
/// IMPORTANT LIMITATION: When UseSession is enabled with a SessionId, the driver creates
13+
/// a single-connection HttpClientFactory instead of using a pooled connection. This means
14+
/// all queries in the session will use the same underlying HTTP connection, which is not
15+
/// suitable for high-performance or high-concurrency scenarios.
16+
///
17+
/// Making queries using the same id from multiple connections simultaneously will cause errors.
18+
///
19+
/// Consider using regular tables with TTL instead of temporary tables
20+
/// if you need to share data across multiple connections
21+
/// </summary>
22+
public static class SessionIdUsage
23+
{
24+
public static async Task Run()
25+
{
26+
Console.WriteLine("Session ID Usage Examples\n");
27+
28+
// Example 1: Using sessions for temporary tables
29+
// To use temporary tables, you must enable sessions
30+
var settings = new ClickHouseClientSettings
31+
{
32+
Host = "localhost",
33+
UseSession = true,
34+
// If you don't set SessionId, a GUID will be automatically generated
35+
};
36+
37+
using var connection = new ClickHouseConnection(settings);
38+
await connection.OpenAsync();
39+
40+
Console.WriteLine($" Session ID: {settings.SessionId}");
41+
42+
// Create a temporary table
43+
// Temporary tables only exist within the session and are automatically dropped
44+
await connection.ExecuteStatementAsync(@"
45+
CREATE TEMPORARY TABLE temp_users
46+
(
47+
id UInt64,
48+
name String,
49+
email String
50+
)
51+
");
52+
Console.WriteLine(" Created temporary table 'temp_users'");
53+
54+
// Insert data into the temporary table
55+
using (var command = connection.CreateCommand())
56+
{
57+
command.CommandText = "INSERT INTO temp_users (id, name, email) VALUES ({id:UInt64}, {name:String}, {email:String})";
58+
command.AddParameter("id", 1UL);
59+
command.AddParameter("name", "Alice");
60+
command.AddParameter("email", "[email protected]");
61+
await command.ExecuteNonQueryAsync();
62+
}
63+
Console.WriteLine(" Inserted data into temporary table");
64+
65+
// Query the temporary table
66+
using (var reader = await connection.ExecuteReaderAsync("SELECT id, name, email FROM temp_users ORDER BY id"))
67+
{
68+
Console.WriteLine("\n Data from temporary table:");
69+
Console.WriteLine(" ID\tName\tEmail");
70+
Console.WriteLine(" --\t----\t-----");
71+
while (reader.Read())
72+
{
73+
var id = reader.GetFieldValue<ulong>(0);
74+
var name = reader.GetString(1);
75+
var email = reader.GetString(2);
76+
Console.WriteLine($" {id}\t{name}\t{email}");
77+
}
78+
}
79+
80+
// Temporary tables are automatically dropped when the connection closes
81+
Console.WriteLine("\n Temporary table will be dropped when connection closes");
82+
83+
Console.WriteLine("\nAll Session ID examples completed!");
84+
}
85+
}

0 commit comments

Comments
 (0)