Skip to content

Commit 0f457b9

Browse files
Solomon external data source bugfixes (#26319)
Co-authored-by: Copilot <[email protected]>
1 parent d2fb053 commit 0f457b9

File tree

10 files changed

+386
-42
lines changed

10 files changed

+386
-42
lines changed

ydb/library/yql/providers/solomon/actors/dq_solomon_read_actor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ class TDqSolomonReadActor : public NActors::TActorBootstrapped<TDqSolomonReadAct
348348

349349
for (size_t i = 0; i < timestamps.size(); ++i){
350350
TInstant timestamp = TInstant::MilliSeconds(timestamps[i]);
351-
if (timestamp < from || timestamp > to) {
351+
if (timestamp < from || timestamp >= to) {
352352
continue;
353353
}
354354

ydb/library/yql/providers/solomon/provider/yql_solomon_dq_integration.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ class TSolomonDqIntegration: public TDqIntegrationBase {
137137
return {};
138138
}
139139
if (!TInstant::TryParseIso8601(value, from)) {
140-
ctx.AddError(TIssue(ctx.GetPosition(settingsRef.Child(i)->Head().Pos()), "couldn't parse `from`, use Iso8601 format, e.g. 2025-03-12T14:40:39Z"));
140+
ctx.AddError(TIssue(ctx.GetPosition(settingsRef.Child(i)->Head().Pos()), "couldn't parse `from`, use ISO8601 format, e.g. 2025-03-12T14:40:39Z"));
141141
return {};
142142
}
143143
continue;
@@ -148,7 +148,7 @@ class TSolomonDqIntegration: public TDqIntegrationBase {
148148
return {};
149149
}
150150
if (!TInstant::TryParseIso8601(value, to)) {
151-
ctx.AddError(TIssue(ctx.GetPosition(settingsRef.Child(i)->Head().Pos()), "couldn't parse `to`, use Iso8601 format, e.g. 2025-03-12T14:40:39Z"));
151+
ctx.AddError(TIssue(ctx.GetPosition(settingsRef.Child(i)->Head().Pos()), "couldn't parse `to`, use ISO8601 format, e.g. 2025-03-12T14:40:39Z"));
152152
return {};
153153
}
154154
continue;

ydb/library/yql/providers/solomon/provider/yql_solomon_load_meta.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,20 @@ class TSolomonLoadTableMetadataTransformer : public TGraphTransformerBase {
6969

7070
TInstant from;
7171
if (auto time = ExtractSetting(settings, "from")) {
72-
from = TInstant::ParseIso8601(*time);
72+
if (!TInstant::TryParseIso8601(*time, from)) {
73+
ctx.AddError(TIssue(ctx.GetPosition(n->Pos()), "couldn't parse `from`, use ISO8601 format, e.g. 2025-03-12T14:40:39Z"));
74+
return TStatus::Error;
75+
}
7376
} else {
74-
from = TInstant::Now() - TDuration::Days(7);
77+
from = TInstant::Zero();
7578
}
7679

7780
TInstant to;
7881
if (auto time = ExtractSetting(settings, "to")) {
79-
to = TInstant::ParseIso8601(*time);
82+
if (!TInstant::TryParseIso8601(*time, to)) {
83+
ctx.AddError(TIssue(ctx.GetPosition(n->Pos()), "couldn't parse `to`, use ISO8601 format, e.g. 2025-03-12T14:40:39Z"));
84+
return TStatus::Error;
85+
}
8086
} else {
8187
to = TInstant::Now();
8288
}

ydb/library/yql/providers/solomon/solomon_accessor/client/solomon_accessor_client.cpp

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -74,32 +74,33 @@ TGetLabelsResponse ProcessGetLabelsResponse(NYql::IHTTPGateway::TResult&& respon
7474
TGetLabelsResult result;
7575

7676
if (response.CurlResponseCode != CURLE_OK) {
77-
return TGetLabelsResponse(TStringBuilder{} << "Error while sending list metric names request to monitoring api: " << response.Issues.ToOneLineString() << " (curl status: " << curl_easy_strerror(response.CurlResponseCode) << ")");
77+
return TGetLabelsResponse(TStringBuilder{} << "Monitoring api get labels response: " << response.Issues.ToOneLineString() <<
78+
", internal code: " << static_cast<int>(response.CurlResponseCode));
7879
}
7980

8081
if (response.Content.HttpResponseCode < 200 || response.Content.HttpResponseCode >= 300) {
81-
return TGetLabelsResponse(TStringBuilder{} << "Error while sending list metric names request to monitoring api: " << response.Content.data() << " (http status: " << response.Content.HttpResponseCode << ")");
82+
return TGetLabelsResponse(TStringBuilder{} << "Monitoring api get labels response: " << response.Content.data() <<
83+
", internal code: " << response.Content.HttpResponseCode);
8284
}
8385

8486
NJson::TJsonValue json;
8587
try {
8688
NJson::ReadJsonTree(response.Content.data(), &json, /*throwOnError*/ true);
8789
} catch (const std::exception& e) {
88-
return TGetLabelsResponse(TStringBuilder{} << "Failed to parse response from monitoring api: " << e.what());
90+
return TGetLabelsResponse("Monitoring api get labels response is not a valid json");
8991
}
9092

9193
if (!json.IsMap() || !json.Has("names") || !json["names"].IsArray()) {
92-
return TGetLabelsResponse("Invalid result from monitoring api");
94+
return TGetLabelsResponse("Monitoring api get labels response doesn't contain requested info");
9395
}
9496

9597
const auto names = json["names"].GetArray();
9698

9799
for (const auto& name : names) {
98100
if (!name.IsString()) {
99-
return TGetLabelsResponse("Invalid label names from monitoring api");
100-
} else {
101-
result.Labels.push_back(name.GetString());
101+
return TGetLabelsResponse("Monitoring api get labels response contains invalid label names");
102102
}
103+
result.Labels.push_back(name.GetString());
103104
}
104105
for (const auto& [key, selector] : knownSelectors) {
105106
result.Labels.push_back(key);
@@ -112,37 +113,39 @@ TListMetricsResponse ProcessListMetricsResponse(NYql::IHTTPGateway::TResult&& re
112113
TListMetricsResult result;
113114

114115
if (response.CurlResponseCode != CURLE_OK) {
115-
return TListMetricsResponse(TStringBuilder{} << "Error while sending list metrics request to monitoring api: " << response.Issues.ToOneLineString());
116+
return TListMetricsResponse(TStringBuilder{} << "Monitoring api list metrics response: " << response.Issues.ToOneLineString() <<
117+
", internal code: " << static_cast<int>(response.CurlResponseCode));
116118
}
117119

118120
if (response.Content.HttpResponseCode < 200 || response.Content.HttpResponseCode >= 300) {
119-
return TListMetricsResponse(TStringBuilder{} << "Error while sending list metrics request to monitoring api: " << response.Content.data());
121+
return TListMetricsResponse(TStringBuilder{} << "Monitoring api list metrics response: " << response.Content.data() <<
122+
", internal code: " << response.Content.HttpResponseCode);
120123
}
121124

122125
NJson::TJsonValue json;
123126
try {
124127
NJson::ReadJsonTree(response.Content.data(), &json, /*throwOnError*/ true);
125128
} catch (const std::exception& e) {
126-
return TListMetricsResponse(TStringBuilder{} << "Failed to parse response from monitoring api: " << e.what());
129+
return TListMetricsResponse("Monitoring api list metrics response is not a valid json" );
127130
}
128131

129132
if (!json.IsMap() || !json.Has("result") || !json.Has("page")) {
130-
return TListMetricsResponse("Invalid list metrics result from monitoring api");
133+
return TListMetricsResponse("Monitoring api list metrics response doesn't contain requested info");
131134
}
132135

133136
const auto pagesInfo = json["page"];
134137
if (!pagesInfo.IsMap() ||
135138
!pagesInfo.Has("pagesCount") || !pagesInfo["pagesCount"].IsInteger() ||
136139
!pagesInfo.Has("totalCount") || !pagesInfo["totalCount"].IsInteger()) {
137-
return TListMetricsResponse("Invalid paging info from monitoring api");
140+
return TListMetricsResponse("Monitoring api list metrics response doesn't contain paging info");
138141
}
139142

140143
result.PagesCount = pagesInfo["pagesCount"].GetInteger();
141144
result.TotalCount = pagesInfo["totalCount"].GetInteger();
142145

143146
for (const auto& metricObj : json["result"].GetArray()) {
144147
if (!metricObj.IsMap() || !metricObj.Has("labels") || !metricObj["labels"].IsMap() || !metricObj.Has("type") || !metricObj["type"].IsString()) {
145-
return TListMetricsResponse("Invalid list metrics result from monitoring api");
148+
return TListMetricsResponse("Monitoring api list metrics response contains invalid metrics");
146149
}
147150

148151
TSelectors selectors;
@@ -173,22 +176,24 @@ TGetPointsCountResponse ProcessGetPointsCountResponse(NYql::IHTTPGateway::TResul
173176
}
174177
}
175178

176-
return TGetPointsCountResponse(TStringBuilder() << "Error while sending points count request to monitoring api: " << issues);
179+
return TGetPointsCountResponse(TStringBuilder() << "Monitoring api points count response: " << issues <<
180+
", internal code: " << static_cast<int>(response.CurlResponseCode));
177181
}
178182

179183
if (response.Content.HttpResponseCode < 200 || response.Content.HttpResponseCode >= 300) {
180-
return TGetPointsCountResponse(TStringBuilder{} << "Error while sending points count request to monitoring api: " << response.Content.data());
184+
return TGetPointsCountResponse(TStringBuilder{} << "Monitoring api points count response: " << response.Content.data() <<
185+
", internal code: " << response.Content.HttpResponseCode);
181186
}
182187

183188
NJson::TJsonValue json;
184189
try {
185190
NJson::ReadJsonTree(response.Content.data(), &json, /*throwOnError*/ true);
186191
} catch (const std::exception& e) {
187-
return TGetPointsCountResponse(TStringBuilder{} << "Failed to parse points count response from monitoring api: " << e.what());
192+
return TGetPointsCountResponse("Monitoring api points count response is not a valid json");
188193
}
189194

190195
if (!json.IsMap() || !json.Has("scalar") || !json["scalar"].IsInteger()) {
191-
return TGetPointsCountResponse("Invalid points count result from monitoring api");
196+
return TGetPointsCountResponse("Monitoring api points count response doesn't contain requested info");
192197
}
193198

194199
result.PointsCount = json["scalar"].GetInteger() + downsampledPointsCount;
@@ -200,15 +205,15 @@ TGetDataResponse ProcessGetDataResponse(NYdbGrpc::TGrpcStatus&& status, ReadResp
200205
TGetDataResult result;
201206

202207
if (!status.Ok()) {
203-
TString error = TStringBuilder{} << "Error while sending data request to monitoring api: " << status.Msg;
208+
TString error = TStringBuilder{} << "Monitoring api get data response: " << status.Msg;
204209
if (status.GRpcStatusCode == grpc::StatusCode::RESOURCE_EXHAUSTED || status.GRpcStatusCode == grpc::StatusCode::UNAVAILABLE) {
205210
return TGetDataResponse(error, EStatus::STATUS_RETRIABLE_ERROR);
206211
}
207212
return TGetDataResponse(error);
208213
}
209214

210215
if (response.response_per_query_size() != 1) {
211-
return TGetDataResponse("Invalid get data repsonse size from monitoring api");
216+
return TGetDataResponse("Monitoring api get data response is invalid");
212217
}
213218

214219
const auto& responseValue = response.response_per_query()[0];
@@ -579,7 +584,7 @@ class TSolomonAccessorClient : public ISolomonAccessorClient, public std::enable
579584
fullSelectors[labelName] = {"=", "-"};
580585
}
581586
}
582-
return selectors;
587+
return fullSelectors;
583588
}
584589

585590
TString BuildSelectorsProgram(const TSelectors& selectors, bool useNewFormat = false) const {

ydb/library/yql/tests/sql/solomon/canondata/test.test_solomon-InvalidProject-_/extracted

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66
<tmp_path>/program.sql:<main>: Error: Fatal Error
77

88

9-
<tmp_path>/program.sql:<main>: Error: Error while sending data request to monitoring api: Project invalid does not exist (appeared 1 time at ISOTIME)
9+
<tmp_path>/program.sql:<main>: Error: Monitoring api get data response: Project invalid does not exist (appeared 1 time at ISOTIME)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Failed to execute query, reason:
22
Request finished with status: EXTERNAL_ERROR
33
Issues:
4-
<main>: Error: Error while sending data request to monitoring api: Project invalid does not exist
4+
<main>: Error: Monitoring api get data response: Project invalid does not exist
55

66

77

ydb/tests/solomon/reading/base.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
class SolomonReadingTestBase(object):
1414
@classmethod
1515
def setup_class(cls):
16-
cls.basic_reading_timestamps = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60]
17-
cls.basic_reading_values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
16+
cls.basic_reading_timestamps = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55]
17+
cls.basic_reading_values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
1818

1919
cls.listing_paging_metrics_size = 1000
2020

@@ -96,7 +96,7 @@ def execute_query(cls, query):
9696

9797
@staticmethod
9898
def _generate_listing_paging_test_metrics(size):
99-
return [
99+
listing_paging_metrics = [
100100
{
101101
"labels" : {"test_type": "listing_paging_test", "test_label": str(i)},
102102
"type" : "DGAUGE",
@@ -106,6 +106,15 @@ def _generate_listing_paging_test_metrics(size):
106106
for i in range(size)
107107
]
108108

109+
listing_paging_metrics.append({
110+
"labels" : {"test_type": "listing_paging_test"},
111+
"type" : "DGAUGE",
112+
"timestamps" : [0],
113+
"values" : [0]
114+
})
115+
116+
return listing_paging_metrics
117+
109118
@staticmethod
110119
def _generate_data_paging_timeseries(size):
111120
timestamps = [i * 5 for i in range(size)]

ydb/tests/solomon/reading/basic_reading.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ def check_query_result(self, result, error, downsampling_disabled):
2727
return False, "values differ from canonical, have {}, should be {}".format(values, canon_values)
2828
return True, None
2929

30+
def check_query_result_size(self, result, error):
31+
if error is not None:
32+
return False, error
33+
34+
result_size = len(result[0].rows)
35+
36+
if (result_size != 1):
37+
return False, "should only have a single return row, have: {}".format(result_size)
38+
39+
return True, None
40+
3041
@link_test_case("#16398")
3142
def test_basic_reading_solomon(self):
3243
data_source_query = f"""
@@ -144,6 +155,18 @@ def test_basic_reading_solomon(self):
144155
assert error is None, error
145156
assert any(column.name == "tt" for column in result[0].columns)
146157

158+
# query with a single second interval
159+
query = """
160+
SELECT * FROM local_solomon.basic_reading WITH (
161+
selectors = @@{cluster="basic_reading", service="my_service", test_type="basic_reading_test"}@@,
162+
163+
from = "1970-01-01T00:00:00Z",
164+
to = "1970-01-01T00:00:01Z"
165+
)
166+
"""
167+
succes, error = self.check_query_result_size(*self.execute_query(query))
168+
assert succes, error
169+
147170
@link_test_case("#23192")
148171
def test_basic_reading_monitoring(self):
149172
data_source_query = f"""
@@ -262,3 +285,15 @@ def test_basic_reading_monitoring(self):
262285
result, error = self.execute_query(query)
263286
assert error is None, error
264287
assert any(column.name == "tt" for column in result[0].columns)
288+
289+
# query with a single second interval
290+
query = """
291+
SELECT * FROM local_monitoring.my_service WITH (
292+
selectors = @@{test_type="basic_reading_test"}@@,
293+
294+
from = "1970-01-01T00:00:00Z",
295+
to = "1970-01-01T00:00:01Z"
296+
)
297+
"""
298+
succes, error = self.check_query_result_size(*self.execute_query(query))
299+
assert succes, error

ydb/tests/solomon/reading/listing_paging.py

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88

99
class TestListingPaging(SolomonReadingTestBase):
10-
def check_listing_paging_result(self, result_set, error):
10+
def check_full_listing_result(self, result_set, error):
1111
if error is not None:
1212
return False, error
1313

@@ -29,6 +29,19 @@ def check_listing_paging_result(self, result_set, error):
2929

3030
return True, None
3131

32+
def check_listing_size(self, result_set, error):
33+
if error is not None:
34+
return False, error
35+
36+
rows = []
37+
for result in result_set:
38+
rows.extend(result.rows)
39+
40+
if (len(rows) != self.listing_paging_metrics_size + 1):
41+
return False, "Result size differs from expected: have {}, should be {}".format(len(rows), self.listing_paging_metrics_size)
42+
43+
return True, None
44+
3245
@link_test_case("#16395")
3346
def test_listing_paging_solomon(self):
3447
data_source_query = f"""
@@ -42,7 +55,6 @@ def test_listing_paging_solomon(self):
4255
result, error = self.execute_query(data_source_query)
4356
assert error is None
4457

45-
# simplest query with default downsampling settings
4658
query = """
4759
SELECT test_label FROM local_solomon.listing_paging WITH (
4860
selectors = @@{cluster="listing_paging", service="my_service", test_type="listing_paging_test", test_label="*"}@@,
@@ -53,7 +65,18 @@ def test_listing_paging_solomon(self):
5365
to = "1970-01-01T00:01:00Z"
5466
)
5567
"""
56-
success, error = self.check_listing_paging_result(*self.execute_query(query))
68+
success, error = self.check_full_listing_result(*self.execute_query(query))
69+
assert success, error
70+
71+
query = """
72+
SELECT * FROM local_solomon.listing_paging WITH (
73+
selectors = @@{cluster="listing_paging", service="my_service", test_type="listing_paging_test"}@@,
74+
75+
from = "1970-01-01T00:00:00Z",
76+
to = "1970-01-01T00:01:00Z"
77+
)
78+
"""
79+
success, error = self.check_listing_size(*self.execute_query(query))
5780
assert success, error
5881

5982
@link_test_case("#23190")
@@ -71,7 +94,6 @@ def test_listing_paging_monitoring(self):
7194
result, error = self.execute_query(data_source_query)
7295
assert error is None
7396

74-
# simplest query with default downsampling settings
7597
query = """
7698
SELECT test_label FROM local_monitoring.my_service WITH (
7799
selectors = @@{test_type="listing_paging_test", test_label="*"}@@,
@@ -82,5 +104,16 @@ def test_listing_paging_monitoring(self):
82104
to = "1970-01-01T00:01:00Z"
83105
)
84106
"""
85-
success, error = self.check_listing_paging_result(*self.execute_query(query))
107+
success, error = self.check_full_listing_result(*self.execute_query(query))
108+
assert success, error
109+
110+
query = """
111+
SELECT * FROM local_monitoring.my_service WITH (
112+
selectors = @@{test_type="listing_paging_test"}@@,
113+
114+
from = "1970-01-01T00:00:00Z",
115+
to = "1970-01-01T00:01:00Z"
116+
)
117+
"""
118+
success, error = self.check_listing_size(*self.execute_query(query))
86119
assert success, error

0 commit comments

Comments
 (0)