clickhouse-native 0.11.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dbc7f982fc96317d6b6721bc7162e22d01eac4f4fc9edb307443d24fdee59032
|
|
4
|
+
data.tar.gz: 824150b24641414bfc0ccc4a6e37aa521b768bb73423c1bda9c1bfbefc466044
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6cda8a41d58816d63aa064ae3008b20980d35be6f695ff04433bc760ec28c0a286b707bdeb725d5691ac8245533c05d90df4c9c5b3eb13af6b860a179d9349db
|
|
7
|
+
data.tar.gz: caf00f391e65eb920a2d917316fa05c1569b7aac7c89f83a122b149c3305a3b0b40b6dc02afa5bd1cebcd78f0897700d5a2f9fbafb2c0d327da973dd3bc542f6
|
|
@@ -872,6 +872,7 @@ struct ExecuteNoGVL {
|
|
|
872
872
|
Client* client;
|
|
873
873
|
const Query* query;
|
|
874
874
|
std::exception_ptr err;
|
|
875
|
+
bool cancelled;
|
|
875
876
|
};
|
|
876
877
|
} // namespace
|
|
877
878
|
|
|
@@ -885,11 +886,16 @@ static void* execute_no_gvl(void* data) {
|
|
|
885
886
|
return nullptr;
|
|
886
887
|
}
|
|
887
888
|
|
|
889
|
+
// Unblock functions run on the *interrupting* thread while the blocked thread
|
|
890
|
+
// is still inside Client::Execute(). CancelInFlight() only shuts the socket
|
|
891
|
+
// down; ResetConnection() would free the very streams that thread is reading
|
|
892
|
+
// (Thread#kill from Parallel.in_threads, Timeout, Sidekiq shutdown), which
|
|
893
|
+
// reads back as a garbage packet type and then segfaults. The blocked call
|
|
894
|
+
// returns EOF, raises, and the pool discards the client.
|
|
888
895
|
static void execute_unblock(void* data) {
|
|
889
|
-
// The only safe abort clickhouse-cpp exposes is tearing the connection.
|
|
890
|
-
// On interrupt we kill the socket; the pool will discard this client.
|
|
891
896
|
auto* a = static_cast<ExecuteNoGVL*>(data);
|
|
892
|
-
|
|
897
|
+
a->cancelled = true;
|
|
898
|
+
try { a->client->CancelInFlight(); } catch (...) {}
|
|
893
899
|
}
|
|
894
900
|
|
|
895
901
|
static VALUE ch_client_execute(int argc, VALUE* argv, VALUE self) {
|
|
@@ -903,13 +909,20 @@ static VALUE ch_client_execute(int argc, VALUE* argv, VALUE self) {
|
|
|
903
909
|
apply_default_settings(q, c);
|
|
904
910
|
apply_settings(q, kwargs);
|
|
905
911
|
|
|
906
|
-
ExecuteNoGVL args{c->client.get(), &q, nullptr};
|
|
912
|
+
ExecuteNoGVL args{c->client.get(), &q, nullptr, false};
|
|
907
913
|
rb_thread_call_without_gvl(execute_no_gvl, &args, execute_unblock, &args);
|
|
908
914
|
if (args.err) {
|
|
909
915
|
// clickhouse-cpp may leave the read stream partially consumed when the
|
|
910
916
|
// server exception or an unsupported-type error is thrown mid-block.
|
|
911
917
|
// Reset so the next call on this Client starts from a clean protocol.
|
|
912
|
-
|
|
918
|
+
// Not after a cancel: that socket is already shut down and the caller
|
|
919
|
+
// discards the client, so the connect+handshake buys nothing. A
|
|
920
|
+
// Thread#kill or Timeout never gets here at all — step 5 of
|
|
921
|
+
// rb_thread_call_without_gvl delivers the interrupt before it returns —
|
|
922
|
+
// so this covers what is left: an unblock function that fires without a
|
|
923
|
+
// longjmp behind it, from a trap handler that does not raise or an
|
|
924
|
+
// interrupt Thread.handle_interrupt has deferred.
|
|
925
|
+
if (!args.cancelled) { try { c->client->ResetConnection(); } catch (...) {} }
|
|
913
926
|
try { std::rethrow_exception(args.err); }
|
|
914
927
|
catch (const std::exception& e) { raise_mapped_ex(e); }
|
|
915
928
|
}
|
|
@@ -1006,6 +1019,7 @@ struct InsertNoGVL {
|
|
|
1006
1019
|
const Block* block;
|
|
1007
1020
|
const std::vector<std::pair<std::string, std::string>>* settings;
|
|
1008
1021
|
std::exception_ptr err;
|
|
1022
|
+
bool cancelled;
|
|
1009
1023
|
};
|
|
1010
1024
|
} // namespace
|
|
1011
1025
|
|
|
@@ -1021,7 +1035,8 @@ static void* insert_no_gvl(void* data) {
|
|
|
1021
1035
|
|
|
1022
1036
|
static void insert_unblock(void* data) {
|
|
1023
1037
|
auto* a = static_cast<InsertNoGVL*>(data);
|
|
1024
|
-
|
|
1038
|
+
a->cancelled = true;
|
|
1039
|
+
try { a->client->CancelInFlight(); } catch (...) {}
|
|
1025
1040
|
}
|
|
1026
1041
|
|
|
1027
1042
|
static VALUE ch_client_insert_block(VALUE self, VALUE rb_table, VALUE rb_columns, VALUE rb_rows) {
|
|
@@ -1078,10 +1093,10 @@ static VALUE ch_client_insert_block(VALUE self, VALUE rb_table, VALUE rb_columns
|
|
|
1078
1093
|
block.AppendColumn(names[i], cols[i]);
|
|
1079
1094
|
}
|
|
1080
1095
|
|
|
1081
|
-
InsertNoGVL args{c->client.get(), &table, &block, &c->default_settings, nullptr};
|
|
1096
|
+
InsertNoGVL args{c->client.get(), &table, &block, &c->default_settings, nullptr, false};
|
|
1082
1097
|
rb_thread_call_without_gvl(insert_no_gvl, &args, insert_unblock, &args);
|
|
1083
1098
|
if (args.err) {
|
|
1084
|
-
try { c->client->ResetConnection(); } catch (...) {}
|
|
1099
|
+
if (!args.cancelled) { try { c->client->ResetConnection(); } catch (...) {} }
|
|
1085
1100
|
try { std::rethrow_exception(args.err); }
|
|
1086
1101
|
catch (const std::exception& e) { raise_mapped_ex(e); }
|
|
1087
1102
|
}
|
|
@@ -1114,6 +1129,7 @@ struct QueryEachNoGVL {
|
|
|
1114
1129
|
const Query* query;
|
|
1115
1130
|
QueryEachState* state;
|
|
1116
1131
|
std::exception_ptr err;
|
|
1132
|
+
bool cancelled;
|
|
1117
1133
|
};
|
|
1118
1134
|
} // namespace
|
|
1119
1135
|
|
|
@@ -1166,7 +1182,8 @@ static void* query_each_no_gvl(void* data) {
|
|
|
1166
1182
|
static void query_each_unblock(void* data) {
|
|
1167
1183
|
auto* a = static_cast<QueryEachNoGVL*>(data);
|
|
1168
1184
|
a->state->aborted = true;
|
|
1169
|
-
|
|
1185
|
+
a->cancelled = true;
|
|
1186
|
+
try { a->client->CancelInFlight(); } catch (...) {}
|
|
1170
1187
|
}
|
|
1171
1188
|
|
|
1172
1189
|
static VALUE ch_client_query_each(int argc, VALUE* argv, VALUE self) {
|
|
@@ -1187,12 +1204,12 @@ static VALUE ch_client_query_each(int argc, VALUE* argv, VALUE self) {
|
|
|
1187
1204
|
rb_thread_call_with_gvl(with_gvl_yield, &ya);
|
|
1188
1205
|
return !state.aborted;
|
|
1189
1206
|
});
|
|
1190
|
-
QueryEachNoGVL args{c->client.get(), &q, &state, nullptr};
|
|
1207
|
+
QueryEachNoGVL args{c->client.get(), &q, &state, nullptr, false};
|
|
1191
1208
|
|
|
1192
1209
|
rb_thread_call_without_gvl(query_each_no_gvl, &args, query_each_unblock, &args);
|
|
1193
1210
|
|
|
1194
1211
|
if (args.err) {
|
|
1195
|
-
try { c->client->ResetConnection(); } catch (...) {}
|
|
1212
|
+
if (!args.cancelled) { try { c->client->ResetConnection(); } catch (...) {} }
|
|
1196
1213
|
if (state.exc_tag) rb_jump_tag(state.exc_tag);
|
|
1197
1214
|
try { std::rethrow_exception(args.err); }
|
|
1198
1215
|
catch (const std::exception& e) { raise_mapped_ex(e); }
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
diff --git a/clickhouse/base/socket.cpp b/clickhouse/base/socket.cpp
|
|
2
|
+
index 3bb1aa5..f188d3f 100644
|
|
3
|
+
--- a/clickhouse/base/socket.cpp
|
|
4
|
+
+++ b/clickhouse/base/socket.cpp
|
|
5
|
+
@@ -345,6 +345,19 @@ Socket::~Socket() {
|
|
6
|
+
Close();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
+void Socket::Cancel() {
|
|
10
|
+
+ if (handle_ == INVALID_SOCKET) {
|
|
11
|
+
+ return;
|
|
12
|
+
+ }
|
|
13
|
+
+ // shutdown(), not close(): the descriptor stays valid, so a concurrent
|
|
14
|
+
+ // reader returns EOF instead of racing a reused fd number.
|
|
15
|
+
+#if defined(_win_)
|
|
16
|
+
+ shutdown(handle_, SD_BOTH);
|
|
17
|
+
+#else
|
|
18
|
+
+ ::shutdown(handle_, SHUT_RDWR);
|
|
19
|
+
+#endif
|
|
20
|
+
+}
|
|
21
|
+
+
|
|
22
|
+
void Socket::Close() {
|
|
23
|
+
CloseSocket(handle_);
|
|
24
|
+
handle_ = INVALID_SOCKET;
|
|
25
|
+
diff --git a/clickhouse/base/socket.h b/clickhouse/base/socket.h
|
|
26
|
+
index 9bd9ca3..0234b1e 100644
|
|
27
|
+
--- a/clickhouse/base/socket.h
|
|
28
|
+
+++ b/clickhouse/base/socket.h
|
|
29
|
+
@@ -80,6 +80,12 @@ public:
|
|
30
|
+
|
|
31
|
+
virtual std::unique_ptr<InputStream> makeInputStream() const = 0;
|
|
32
|
+
virtual std::unique_ptr<OutputStream> makeOutputStream() const = 0;
|
|
33
|
+
+
|
|
34
|
+
+ /// Unblock a thread parked in recv()/send() on this socket. Unlike
|
|
35
|
+
+ /// closing or replacing the socket it destroys nothing, so it is safe
|
|
36
|
+
+ /// to call from another thread while the owner is mid-read: the owner
|
|
37
|
+
+ /// sees EOF and unwinds through its own frames.
|
|
38
|
+
+ virtual void Cancel() {}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@@ -123,6 +129,8 @@ public:
|
|
43
|
+
std::unique_ptr<InputStream> makeInputStream() const override;
|
|
44
|
+
std::unique_ptr<OutputStream> makeOutputStream() const override;
|
|
45
|
+
|
|
46
|
+
+ void Cancel() override;
|
|
47
|
+
+
|
|
48
|
+
protected:
|
|
49
|
+
Socket(const Socket&) = delete;
|
|
50
|
+
Socket& operator = (const Socket&) = delete;
|
|
51
|
+
diff --git a/clickhouse/client.cpp b/clickhouse/client.cpp
|
|
52
|
+
index c9732f4..88c9b84 100644
|
|
53
|
+
--- a/clickhouse/client.cpp
|
|
54
|
+
+++ b/clickhouse/client.cpp
|
|
55
|
+
@@ -9,6 +9,7 @@
|
|
56
|
+
#include "columns/factory.h"
|
|
57
|
+
|
|
58
|
+
#include <cassert>
|
|
59
|
+
+#include <mutex>
|
|
60
|
+
#include <optional>
|
|
61
|
+
#include <sstream>
|
|
62
|
+
#include <system_error>
|
|
63
|
+
@@ -226,6 +227,8 @@ public:
|
|
64
|
+
|
|
65
|
+
void ResetConnection();
|
|
66
|
+
|
|
67
|
+
+ void CancelInFlight();
|
|
68
|
+
+
|
|
69
|
+
void ResetConnectionEndpoint();
|
|
70
|
+
|
|
71
|
+
const ServerInfo& GetServerInfo() const;
|
|
72
|
+
@@ -313,6 +316,11 @@ private:
|
|
73
|
+
|
|
74
|
+
std::unique_ptr<SocketFactory> socket_factory_;
|
|
75
|
+
|
|
76
|
+
+ /// Guards socket_ against CancelInFlight() on another thread. Covers the
|
|
77
|
+
+ /// swap in ResetConnection() and the displaced socket's destructor, so a
|
|
78
|
+
+ /// canceller can never shutdown() an fd that is closing or already reissued.
|
|
79
|
+
+ std::mutex socket_mutex_;
|
|
80
|
+
+
|
|
81
|
+
std::unique_ptr<InputStream> input_;
|
|
82
|
+
std::unique_ptr<OutputStream> output_;
|
|
83
|
+
std::unique_ptr<SocketBase> socket_;
|
|
84
|
+
@@ -610,8 +618,24 @@ void Client::Impl::Ping() {
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
+void Client::Impl::CancelInFlight() {
|
|
89
|
+
+ std::lock_guard<std::mutex> lock(socket_mutex_);
|
|
90
|
+
+ if (socket_) {
|
|
91
|
+
+ socket_->Cancel();
|
|
92
|
+
+ }
|
|
93
|
+
+}
|
|
94
|
+
+
|
|
95
|
+
void Client::Impl::ResetConnection() {
|
|
96
|
+
- InitializeStreams(socket_factory_->connect(options_, current_endpoint_.value()));
|
|
97
|
+
+ auto fresh = socket_factory_->connect(options_, current_endpoint_.value());
|
|
98
|
+
+ {
|
|
99
|
+
+ // InitializeStreams swaps, so `fresh` comes back holding the *old*
|
|
100
|
+
+ // socket; reset it here so its destructor runs under the lock too.
|
|
101
|
+
+ // Left to itself it would die in this function's full-expression,
|
|
102
|
+
+ // outside any lock, which is the window CancelInFlight() must not hit.
|
|
103
|
+
+ std::lock_guard<std::mutex> lock(socket_mutex_);
|
|
104
|
+
+ InitializeStreams(std::move(fresh));
|
|
105
|
+
+ fresh.reset();
|
|
106
|
+
+ }
|
|
107
|
+
state_ = State::Idle;
|
|
108
|
+
|
|
109
|
+
if (!Handshake()) {
|
|
110
|
+
@@ -1408,6 +1432,10 @@ void Client::ResetConnection() {
|
|
111
|
+
impl_->ResetConnection();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
+void Client::CancelInFlight() {
|
|
115
|
+
+ impl_->CancelInFlight();
|
|
116
|
+
+}
|
|
117
|
+
+
|
|
118
|
+
void Client::ResetConnectionEndpoint() {
|
|
119
|
+
impl_->ResetConnectionEndpoint();
|
|
120
|
+
}
|
|
121
|
+
diff --git a/clickhouse/client.h b/clickhouse/client.h
|
|
122
|
+
index 6c6ff53..27be58c 100644
|
|
123
|
+
--- a/clickhouse/client.h
|
|
124
|
+
+++ b/clickhouse/client.h
|
|
125
|
+
@@ -326,6 +326,11 @@ public:
|
|
126
|
+
/// Reset connection with initial params.
|
|
127
|
+
void ResetConnection();
|
|
128
|
+
|
|
129
|
+
+ /// Unblock a thread parked in an Execute()/Insert() socket read.
|
|
130
|
+
+ /// Destroys nothing, so unlike ResetConnection() it is safe to call
|
|
131
|
+
+ /// from another thread while a query is still in flight.
|
|
132
|
+
+ void CancelInFlight();
|
|
133
|
+
+
|
|
134
|
+
const ServerInfo& GetServerInfo() const;
|
|
135
|
+
|
|
136
|
+
/// Get current connected endpoint.
|
|
@@ -22,8 +22,8 @@ module ClickhouseNative
|
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
# On
|
|
26
|
-
#
|
|
25
|
+
# On an aborted operation, discard the client rather than reuse it: the
|
|
26
|
+
# socket is left in an unknown state. The C++ binding issues
|
|
27
27
|
# ResetConnection, but a subsequent send can still surface buffered
|
|
28
28
|
# protocol errors from the prior aborted operation — those get
|
|
29
29
|
# attributed to whatever SQL we tried next, producing misleading log
|
|
@@ -44,15 +44,18 @@ module ClickhouseNative
|
|
|
44
44
|
# before running the query, so most stale connections never surface as
|
|
45
45
|
# a ConnectionError here at all. This retry still covers the residual
|
|
46
46
|
# race (socket dies between the ping and the query).
|
|
47
|
+
#
|
|
48
|
+
# A bare `rescue` is not enough to spot an abandoned query. Timeout and
|
|
49
|
+
# Sidekiq shutdown raise off Exception rather than StandardError, and
|
|
50
|
+
# Thread#kill raises nothing at all — Parallel.in_threads kills every
|
|
51
|
+
# sibling worker the moment one of them fails. Miss those and the
|
|
52
|
+
# connection goes back in with the server still streaming a response at
|
|
53
|
+
# it; the next checkout reads that leftover as its own, which surfaces
|
|
54
|
+
# as a bogus packet type far from here.
|
|
47
55
|
def with
|
|
48
56
|
attempts = 0
|
|
49
57
|
begin
|
|
50
|
-
@pool.with
|
|
51
|
-
yield client
|
|
52
|
-
rescue
|
|
53
|
-
@pool.discard_current_connection(&:close)
|
|
54
|
-
raise
|
|
55
|
-
end
|
|
58
|
+
@pool.with { |client| discard_unless_clean { yield client } }
|
|
56
59
|
rescue ConnectionError
|
|
57
60
|
attempts += 1
|
|
58
61
|
retry if attempts == 1
|
|
@@ -91,5 +94,30 @@ module ClickhouseNative
|
|
|
91
94
|
def describe_table(table, db_name: nil)
|
|
92
95
|
with { |c| c.describe_table(table, db_name:) }
|
|
93
96
|
end
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
|
|
100
|
+
# Keep the client only when the block either finished or left of its own
|
|
101
|
+
# accord. A `break` out of a streaming read is deliberate, and the binding
|
|
102
|
+
# has already reconnected the client on its way out (query_each resets the
|
|
103
|
+
# connection before rb_jump_tag), so it is good to reuse — discarding would
|
|
104
|
+
# buy a second connect on top of the one already paid. An exception is an
|
|
105
|
+
# abort, and so is Thread#kill: it raises nothing, so no rescue ever sees
|
|
106
|
+
# it, and its only trace is an "aborting" thread while ensure runs.
|
|
107
|
+
def discard_unless_clean
|
|
108
|
+
finished = false
|
|
109
|
+
begin
|
|
110
|
+
result = yield
|
|
111
|
+
finished = true
|
|
112
|
+
result
|
|
113
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
|
114
|
+
@pool.discard_current_connection(&:close)
|
|
115
|
+
raise
|
|
116
|
+
ensure
|
|
117
|
+
if !finished && Thread.current.status == "aborting"
|
|
118
|
+
@pool.discard_current_connection(&:close)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
94
122
|
end
|
|
95
123
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: clickhouse-native
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.11.
|
|
4
|
+
version: 0.11.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yuri Smirnov
|
|
@@ -36,6 +36,7 @@ files:
|
|
|
36
36
|
- ext/clickhouse_native/extconf.rb
|
|
37
37
|
- ext/clickhouse_native/patches/0001-preserve-declared-column-type.patch
|
|
38
38
|
- ext/clickhouse_native/patches/0002-carry-settings-into-insert.patch
|
|
39
|
+
- ext/clickhouse_native/patches/0003-cancelable-socket.patch
|
|
39
40
|
- ext/clickhouse_native/vendor/clickhouse-cpp/.clang-format
|
|
40
41
|
- ext/clickhouse_native/vendor/clickhouse-cpp/.git
|
|
41
42
|
- ext/clickhouse_native/vendor/clickhouse-cpp/.gitattributes
|