1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <fb303/ServiceData.h>
20 #include <fb303/detail/QuantileStatWrappers.h>
21 #include <folly/synchronization/Hazptr.h>
22 
23 namespace facebook::fb303::detail {
24 
25 template <size_t N>
26 class DynamicQuantileStatWrapper<N>::MapHolder
27     : public folly::hazptr_obj_base<DynamicQuantileStatWrapper<N>::MapHolder> {
28  public:
29   std::unordered_map<
30       DynamicQuantileStatWrapper<N>::SubkeyArray,
31       std::shared_ptr<QuantileStat>,
32       DynamicQuantileStatWrapper<N>::SubkeyArrayHash>
33       map;
34 };
35 
36 template <size_t N>
DynamicQuantileStatWrapper(std::string keyFormat,folly::Range<const ExportType * > stats,folly::Range<const double * > quantiles,folly::Range<const size_t * > timeseriesLengths)37 DynamicQuantileStatWrapper<N>::DynamicQuantileStatWrapper(
38     std::string keyFormat,
39     folly::Range<const ExportType*> stats,
40     folly::Range<const double*> quantiles,
41     folly::Range<const size_t*> timeseriesLengths)
42     : format_(std::move(keyFormat)), stats_(new MapHolder()) {
43   spec_.stats.insert(spec_.stats.end(), stats.begin(), stats.end());
44   spec_.quantiles.insert(
45       spec_.quantiles.end(), quantiles.begin(), quantiles.end());
46   spec_.timeseriesLengths.insert(
47       spec_.timeseriesLengths.end(),
48       timeseriesLengths.begin(),
49       timeseriesLengths.end());
50 }
51 
52 template <size_t N>
53 template <typename... Args>
addValue(double value,std::chrono::steady_clock::time_point now,Args &&...subkeys)54 void DynamicQuantileStatWrapper<N>::addValue(
55     double value,
56     std::chrono::steady_clock::time_point now,
57     Args&&... subkeys) {
58   const SubkeyArray subkeyArray{{std::forward<Args>(subkeys)...}};
59 
60   folly::hazptr_holder<> h = folly::make_hazard_pointer<>();
61   auto mapPtr = h.protect(stats_);
62 
63   auto it = mapPtr->map.find(subkeyArray);
64   if (it != mapPtr->map.end()) {
65     it->second->addValue(value, now);
66     return;
67   }
68 
69   auto key = folly::svformat(format_, SubkeyArray(subkeyArray));
70   auto stat = ServiceData::get()->getQuantileStat(
71       key, spec_.stats, spec_.quantiles, spec_.timeseriesLengths);
72 
73   MapHolder* newMap = nullptr;
74   do {
75     if (newMap) {
76       delete newMap;
77     }
78     mapPtr = h.protect(stats_);
79     newMap = new MapHolder(*mapPtr);
80     newMap->map[subkeyArray] = stat;
81   } while (
82       !stats_.compare_exchange_weak(mapPtr, newMap, std::memory_order_acq_rel));
83   h.reset_protection();
84   mapPtr->retire();
85   stat->addValue(value, now);
86 }
87 
88 template <size_t N>
89 template <typename... Args>
addValue(double value,Args &&...subkeys)90 void DynamicQuantileStatWrapper<N>::addValue(double value, Args&&... subkeys) {
91   addValue(
92       value, std::chrono::steady_clock::now(), std::forward<Args>(subkeys)...);
93 }
94 
95 } // namespace facebook::fb303::detail
96