Skip to content
This repository was archived by the owner on Jul 31, 2023. It is now read-only.

Commit a44fa1c

Browse files
author
Ian Sturdy
authored
Add deltaproducer harvesting. (#114)
1 parent 4e6891f commit a44fa1c

9 files changed

Lines changed: 501 additions & 20 deletions

File tree

opencensus/stats/BUILD

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,16 @@ cc_test(
134134
],
135135
)
136136

137+
cc_test(
138+
name = "delta_producer_stats_test",
139+
srcs = ["internal/delta_producer_stats_test.cc"],
140+
copts = TEST_COPTS,
141+
deps = [
142+
":core",
143+
"@com_google_googletest//:gtest_main",
144+
],
145+
)
146+
137147
cc_test(
138148
name = "distribution_test",
139149
srcs = ["internal/distribution_test.cc"],

opencensus/stats/internal/delta_producer.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ void DeltaProducer::SwapDeltas() {
123123
}
124124

125125
void DeltaProducer::ConsumeLastDelta() {
126-
// TODO: implement.
126+
StatsManager::Get()->MergeDelta(last_delta_);
127127
last_delta_.clear();
128128
}
129129

Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
// Copyright 2017, OpenCensus Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "gmock/gmock.h"
16+
#include "gtest/gtest.h"
17+
#include "opencensus/stats/internal/delta_producer.h"
18+
#include "opencensus/stats/measure.h"
19+
#include "opencensus/stats/measure_registry.h"
20+
#include "opencensus/stats/view.h"
21+
22+
namespace opencensus {
23+
namespace stats {
24+
namespace {
25+
26+
constexpr char kFirstMeasureId[] = "first_measure_name";
27+
constexpr char kSecondMeasureId[] = "second_measure_name";
28+
29+
MeasureDouble FirstMeasure() {
30+
static MeasureDouble measure = MeasureRegistry::RegisterDouble(
31+
kFirstMeasureId, "ops", "Usage of resource 1.");
32+
return measure;
33+
}
34+
35+
MeasureInt SecondMeasure() {
36+
static MeasureInt measure = MeasureRegistry::RegisterInt(
37+
kSecondMeasureId, "ops", "Usage of resource 2.");
38+
return measure;
39+
}
40+
41+
// These tests use the public stats interfaces, View and Measure--these are a
42+
// thin layer around the StatsManager.
43+
class StatsManagerTest : public ::testing::Test {
44+
protected:
45+
void SetUp() {
46+
// Access measures to be sure they are initialized.
47+
FirstMeasure();
48+
SecondMeasure();
49+
DeltaProducer::Get()->Flush();
50+
}
51+
52+
const std::string key1_ = "key1";
53+
const std::string key2_ = "key2";
54+
const std::string key3_ = "key3";
55+
};
56+
57+
TEST_F(StatsManagerTest, Count) {
58+
ViewDescriptor view_descriptor = ViewDescriptor()
59+
.set_measure(kFirstMeasureId)
60+
.set_name("count")
61+
.set_aggregation(Aggregation::Count())
62+
.add_column(key1_)
63+
.add_column(key2_);
64+
View view(view_descriptor);
65+
ASSERT_EQ(ViewData::Type::kInt64, view.GetData().type());
66+
EXPECT_TRUE(view.GetData().int_data().empty());
67+
68+
// Stats under a different measure should be ignored.
69+
ExperimentalDeltaProducerRecord({{SecondMeasure(), 1}});
70+
DeltaProducer::Get()->Flush();
71+
EXPECT_TRUE(view.GetData().int_data().empty());
72+
73+
ExperimentalDeltaProducerRecord(
74+
{{FirstMeasure(), 2.0}, {FirstMeasure(), 3.0}});
75+
ExperimentalDeltaProducerRecord(
76+
{{FirstMeasure(), 4.0}},
77+
{{key1_, "value1"}, {key2_, "value2"}, {key3_, "value3"}});
78+
DeltaProducer::Get()->Flush();
79+
const opencensus::stats::ViewData data = view.GetData();
80+
EXPECT_THAT(
81+
data.int_data(),
82+
::testing::UnorderedElementsAre(
83+
::testing::Pair(::testing::ElementsAre("", ""), 2.0),
84+
::testing::Pair(::testing::ElementsAre("value1", "value2"), 1.0)));
85+
}
86+
87+
TEST_F(StatsManagerTest, Sum) {
88+
ViewDescriptor view_descriptor = ViewDescriptor()
89+
.set_measure(kSecondMeasureId)
90+
.set_name("sum")
91+
.set_aggregation(Aggregation::Sum())
92+
.add_column(key1_)
93+
.add_column(key2_);
94+
View view(view_descriptor);
95+
ASSERT_EQ(ViewData::Type::kDouble, view.GetData().type());
96+
EXPECT_TRUE(view.GetData().double_data().empty());
97+
98+
// Stats under a different measure should be ignored.
99+
ExperimentalDeltaProducerRecord({{FirstMeasure(), 1.0}});
100+
DeltaProducer::Get()->Flush();
101+
EXPECT_TRUE(view.GetData().double_data().empty());
102+
103+
ExperimentalDeltaProducerRecord({{SecondMeasure(), 2}, {SecondMeasure(), 3}});
104+
ExperimentalDeltaProducerRecord(
105+
{{SecondMeasure(), 4}},
106+
{{key1_, "value1"}, {key2_, "value2"}, {key3_, "value3"}});
107+
DeltaProducer::Get()->Flush();
108+
const opencensus::stats::ViewData data = view.GetData();
109+
EXPECT_THAT(
110+
data.double_data(),
111+
::testing::UnorderedElementsAre(
112+
::testing::Pair(::testing::ElementsAre("", ""), 5.0),
113+
::testing::Pair(::testing::ElementsAre("value1", "value2"), 4.0)));
114+
}
115+
116+
TEST_F(StatsManagerTest, Distribution) {
117+
ViewDescriptor view_descriptor =
118+
ViewDescriptor()
119+
.set_measure(kSecondMeasureId)
120+
.set_name("distribution")
121+
.set_aggregation(
122+
Aggregation::Distribution(BucketBoundaries::Explicit({10})))
123+
.add_column(key1_)
124+
.add_column(key2_);
125+
View view(view_descriptor);
126+
ASSERT_EQ(ViewData::Type::kDistribution, view.GetData().type());
127+
EXPECT_TRUE(view.GetData().distribution_data().empty());
128+
129+
// Stats under a different measure should be ignored.
130+
ExperimentalDeltaProducerRecord({{FirstMeasure(), 1.0}});
131+
DeltaProducer::Get()->Flush();
132+
EXPECT_TRUE(view.GetData().distribution_data().empty());
133+
134+
ExperimentalDeltaProducerRecord(
135+
{{SecondMeasure(), 5}, {SecondMeasure(), 15}});
136+
ExperimentalDeltaProducerRecord(
137+
{{SecondMeasure(), 5}},
138+
{{key1_, "value1"}, {key2_, "value2"}, {key3_, "value3"}});
139+
DeltaProducer::Get()->Flush();
140+
const opencensus::stats::ViewData data = view.GetData();
141+
EXPECT_EQ(2, data.distribution_data().size());
142+
EXPECT_THAT(data.distribution_data().find({"", ""})->second.bucket_counts(),
143+
::testing::ElementsAre(1, 1));
144+
EXPECT_THAT(data.distribution_data()
145+
.find({"value1", "value2"})
146+
->second.bucket_counts(),
147+
::testing::ElementsAre(1, 0));
148+
}
149+
150+
// TODO: Test window expiration if we add a simulated clock.
151+
TEST_F(StatsManagerTest, IntervalCount) {
152+
ViewDescriptor view_descriptor = ViewDescriptor()
153+
.set_measure(kFirstMeasureId)
154+
.set_name("interval-count")
155+
.set_aggregation(Aggregation::Count())
156+
.add_column(key1_)
157+
.add_column(key2_);
158+
SetAggregationWindow(AggregationWindow::Interval(absl::Minutes(1)),
159+
&view_descriptor);
160+
View view(view_descriptor);
161+
ASSERT_EQ(ViewData::Type::kDouble, view.GetData().type());
162+
EXPECT_TRUE(view.GetData().double_data().empty());
163+
164+
// Stats under a different measure should be ignored.
165+
ExperimentalDeltaProducerRecord({{SecondMeasure(), 1}});
166+
DeltaProducer::Get()->Flush();
167+
EXPECT_TRUE(view.GetData().double_data().empty());
168+
169+
ExperimentalDeltaProducerRecord(
170+
{{FirstMeasure(), 2.0}, {FirstMeasure(), 3.0}});
171+
ExperimentalDeltaProducerRecord(
172+
{{FirstMeasure(), 4.0}},
173+
{{key1_, "value1"}, {key2_, "value2"}, {key3_, "value3"}});
174+
DeltaProducer::Get()->Flush();
175+
const opencensus::stats::ViewData data = view.GetData();
176+
EXPECT_THAT(
177+
data.double_data(),
178+
::testing::UnorderedElementsAre(
179+
::testing::Pair(::testing::ElementsAre("", ""), 2.0),
180+
::testing::Pair(::testing::ElementsAre("value1", "value2"), 1.0)));
181+
}
182+
183+
TEST_F(StatsManagerTest, IntervalSum) {
184+
ViewDescriptor view_descriptor = ViewDescriptor()
185+
.set_measure(kSecondMeasureId)
186+
.set_name("interval-sum")
187+
.set_aggregation(Aggregation::Sum())
188+
.add_column(key1_)
189+
.add_column(key2_);
190+
SetAggregationWindow(AggregationWindow::Interval(absl::Minutes(1)),
191+
&view_descriptor);
192+
View view(view_descriptor);
193+
ASSERT_EQ(ViewData::Type::kDouble, view.GetData().type());
194+
EXPECT_TRUE(view.GetData().double_data().empty());
195+
196+
// Stats under a different measure should be ignored.
197+
ExperimentalDeltaProducerRecord({{FirstMeasure(), 1.0}});
198+
DeltaProducer::Get()->Flush();
199+
EXPECT_TRUE(view.GetData().double_data().empty());
200+
201+
ExperimentalDeltaProducerRecord({{SecondMeasure(), 2}, {SecondMeasure(), 3}});
202+
ExperimentalDeltaProducerRecord(
203+
{{SecondMeasure(), 4}},
204+
{{key1_, "value1"}, {key2_, "value2"}, {key3_, "value3"}});
205+
DeltaProducer::Get()->Flush();
206+
const opencensus::stats::ViewData data = view.GetData();
207+
EXPECT_THAT(
208+
data.double_data(),
209+
::testing::UnorderedElementsAre(
210+
::testing::Pair(::testing::ElementsAre("", ""), 5.0),
211+
::testing::Pair(::testing::ElementsAre("value1", "value2"), 4.0)));
212+
}
213+
214+
TEST_F(StatsManagerTest, IntervalDistribution) {
215+
ViewDescriptor view_descriptor =
216+
ViewDescriptor()
217+
.set_measure(kSecondMeasureId)
218+
.set_name("distribution-interval")
219+
.set_aggregation(
220+
Aggregation::Distribution(BucketBoundaries::Explicit({10})))
221+
.add_column(key1_)
222+
.add_column(key2_);
223+
SetAggregationWindow(AggregationWindow::Interval(absl::Hours(1)),
224+
&view_descriptor);
225+
View view(view_descriptor);
226+
ASSERT_EQ(ViewData::Type::kDistribution, view.GetData().type());
227+
EXPECT_TRUE(view.GetData().distribution_data().empty());
228+
229+
// Stats under a different measure should be ignored.
230+
ExperimentalDeltaProducerRecord({{FirstMeasure(), 1.0}});
231+
DeltaProducer::Get()->Flush();
232+
EXPECT_TRUE(view.GetData().distribution_data().empty());
233+
234+
ExperimentalDeltaProducerRecord(
235+
{{SecondMeasure(), 5}, {SecondMeasure(), 15}});
236+
ExperimentalDeltaProducerRecord(
237+
{{SecondMeasure(), 5}},
238+
{{key1_, "value1"}, {key2_, "value2"}, {key3_, "value3"}});
239+
DeltaProducer::Get()->Flush();
240+
const opencensus::stats::ViewData data = view.GetData();
241+
EXPECT_EQ(2, data.distribution_data().size());
242+
EXPECT_EQ(std::vector<uint64_t>({1, 1}),
243+
data.distribution_data().find({"", ""})->second.bucket_counts());
244+
EXPECT_EQ(std::vector<uint64_t>({1, 0}), data.distribution_data()
245+
.find({"value1", "value2"})
246+
->second.bucket_counts());
247+
}
248+
249+
TEST_F(StatsManagerTest, IdenticalViews) {
250+
ViewDescriptor view_descriptor = ViewDescriptor()
251+
.set_measure(kFirstMeasureId)
252+
.set_name("count")
253+
.set_aggregation(Aggregation::Count())
254+
.add_column(key1_);
255+
256+
ExperimentalDeltaProducerRecord({{FirstMeasure(), 1.0}});
257+
DeltaProducer::Get()->Flush();
258+
{
259+
View view1(view_descriptor);
260+
// No data should be recorded from before the first view is created.
261+
DeltaProducer::Get()->Flush();
262+
EXPECT_TRUE(view1.GetData().int_data().empty());
263+
ExperimentalDeltaProducerRecord({{FirstMeasure(), 1.0}});
264+
DeltaProducer::Get()->Flush();
265+
EXPECT_THAT(view1.GetData().int_data(),
266+
::testing::UnorderedElementsAre(
267+
::testing::Pair(::testing::ElementsAre(""), 1)));
268+
{
269+
View view2(view_descriptor);
270+
ExperimentalDeltaProducerRecord({{FirstMeasure(), 1.0}});
271+
// Second views should mirror the data of the first.
272+
DeltaProducer::Get()->Flush();
273+
EXPECT_THAT(view1.GetData().int_data(),
274+
::testing::UnorderedElementsAre(
275+
::testing::Pair(::testing::ElementsAre(""), 2)));
276+
EXPECT_THAT(view2.GetData().int_data(),
277+
::testing::UnorderedElementsAre(
278+
::testing::Pair(::testing::ElementsAre(""), 2)));
279+
}
280+
// Removing the second view should not affect data from the first.
281+
DeltaProducer::Get()->Flush();
282+
EXPECT_THAT(view1.GetData().int_data(),
283+
::testing::UnorderedElementsAre(
284+
::testing::Pair(::testing::ElementsAre(""), 2)));
285+
}
286+
// A view created after deconstructing all previous views should have data
287+
// reset.
288+
View view(view_descriptor);
289+
DeltaProducer::Get()->Flush();
290+
EXPECT_TRUE(view.GetData().int_data().empty());
291+
}
292+
293+
TEST(StatsManagerDeathTest, UnregisteredMeasure) {
294+
const std::string measure_name = "new_measure_name";
295+
ViewDescriptor view_descriptor = ViewDescriptor()
296+
.set_measure(measure_name)
297+
.set_name("count")
298+
.set_aggregation(Aggregation::Count());
299+
300+
View view(view_descriptor);
301+
EXPECT_FALSE(view.IsValid());
302+
DeltaProducer::Get()->Flush();
303+
// Getting data from an invalid view DCHECKs, and returns empty data in opt
304+
// mode.
305+
EXPECT_DEBUG_DEATH({ EXPECT_TRUE(view.GetData().int_data().empty()); }, "");
306+
// Even if we later register the measure and record data under it, the view
307+
// should still be invalid.
308+
static MeasureDouble measure =
309+
MeasureRegistry::RegisterDouble(measure_name, "", "");
310+
EXPECT_TRUE(measure.IsValid());
311+
ExperimentalDeltaProducerRecord({{measure, 1.0}});
312+
EXPECT_FALSE(view.IsValid());
313+
DeltaProducer::Get()->Flush();
314+
EXPECT_DEBUG_DEATH({ EXPECT_TRUE(view.GetData().int_data().empty()); }, "");
315+
}
316+
317+
} // namespace
318+
} // namespace stats
319+
} // namespace opencensus

0 commit comments

Comments
 (0)