1 #ifndef PUBSEQ_GATEWAY_TIMING__HPP
2 #define PUBSEQ_GATEWAY_TIMING__HPP
3 
4 /*  $Id: timing.hpp 629837 2021-04-22 12:47:49Z ivanov $
5  * ===========================================================================
6  *
7  *                            PUBLIC DOMAIN NOTICE
8  *               National Center for Biotechnology Information
9  *
10  *  This software/database is a "United States Government Work" under the
11  *  terms of the United States Copyright Act.  It was written as part of
12  *  the author's official duties as a United States Government employee and
13  *  thus cannot be copyrighted.  This software/database is freely available
14  *  to the public for use. The National Library of Medicine and the U.S.
15  *  Government have not placed any restriction on its use or reproduction.
16  *
17  *  Although all reasonable efforts have been taken to ensure the accuracy
18  *  and reliability of the software and data, the NLM and the U.S.
19  *  Government do not and cannot warrant the performance or results that
20  *  may be obtained by using this software or data. The NLM and the U.S.
21  *  Government disclaim all warranties, express or implied, including
22  *  warranties of performance, merchantability or fitness for any particular
23  *  purpose.
24  *
25  *  Please cite the author in any work or product based on this material.
26  *
27  * ===========================================================================
28  *
29  * Authors:  Sergey Satskiy
30  *
31  * File Description:
32  *   PSG server timing
33  *
34  */
35 
36 #include "pubseq_gateway_types.hpp"
37 #include "psgs_request.hpp"
38 
39 #include <vector>
40 #include <mutex>
41 using namespace std;
42 
43 #include <util/data_histogram.hpp>
44 #include <connect/services/json_over_uttp.hpp>
45 USING_NCBI_SCOPE;
46 
47 
48 const unsigned long     kMinStatValue = 1;
49 const unsigned long     kMaxStatValue = 16 * 1024 * 1024;
50 const unsigned long     kNStatBins = 24;
51 const string            kStatScaleType = "log";
52 const unsigned long     kTickSpan = 10;
53 
54 
55 enum EPSGOperationStatus {
56     eOpStatusFound,
57     eOpStatusNotFound
58 };
59 
60 enum EPSGOperation {
61     // Low level: more or less monolit operations
62     eLookupLmdbSi2csi,              // (2)
63     eLookupLmdbBioseqInfo,          // (2)
64     eLookupLmdbBlobProp,            // (2)
65     eLookupCassSi2csi,              // (2)
66     eLookupCassBioseqInfo,          // (2)
67     eLookupCassBlobProp,            // (2)
68 
69     eResolutionLmdb,                // (2) From request start
70     eResolutionCass,                // (2) From cassandra start
71 
72     eResolutionError,               // (1) From request start
73     eResolutionNotFound,            // (1) From request start
74     eResolutionFound,               // (1) From request start
75     eResolutionFoundInCassandra,    // (5) From cassandra start
76 
77     eBlobRetrieve,
78     eNARetrieve,
79 
80     eSplitHistoryRetrieve,
81     ePublicCommentRetrieve
82 };
83 
84 
85 // Timing is registered in microseconds, i.e. in integers
86 // Technically speaking the counters will be updated from many threads and
87 // something could be missed. However it seems reasonable not to use atomic
88 // counters because:
89 // - even if something is lost it is not a big deal
90 // - the losses are going to be miserable
91 // - atomic counters may introduce some delays
92 typedef CHistogram<uint64_t, uint64_t, uint64_t>            TOnePSGTiming;
93 typedef CHistogramTimeSeries<uint64_t, uint64_t, uint64_t>  TPSGTiming;
94 
95 
96 // Returns a serialized dictionary
97 CJsonNode SerializeHistogram(const TOnePSGTiming &  histogram,
98                              const string &  name,
99                              const string &  description);
100 
101 
102 // The base class for all the collected statistics.
103 // Individual type of operations will derive from it so that they will be able
104 // to tune the nins and intervals as they need.
105 class CPSGTimingBase
106 {
107     public:
CPSGTimingBase()108         CPSGTimingBase() {}
~CPSGTimingBase()109         virtual ~CPSGTimingBase() {}
110 
111     public:
Add(uint64_t mks)112         void Add(uint64_t   mks)
113         {
114             if (m_PSGTiming)
115                 m_PSGTiming->Add(mks);
116         }
117 
Reset(void)118         void Reset(void)
119         {
120             if (m_PSGTiming)
121                 m_PSGTiming->Reset();
122         }
123 
Rotate(void)124         void Rotate(void)
125         {
126             if (m_PSGTiming)
127                 m_PSGTiming->Rotate();
128         }
129 
GetNumberOfCoveredTicks(void) const130         TPSGTiming::TTicks  GetNumberOfCoveredTicks(void) const
131         {
132             if (m_PSGTiming) {
133                 // The histogram series counts ticks starting from 0 so even
134                 // before the first tick some data may be collected. So +1
135                 // is added here.
136                 return m_PSGTiming->GetCurrentTick() + 1;
137             }
138             return 0;
139         }
140 
141         // Generic serialization to json
142         virtual CJsonNode SerializeCombined(int  most_ancient_time,
143                                             int  most_recent_time,
144                                             unsigned long  tick_span,
145                                             const string &  name,
146                                             const string &  description) const;
147         virtual CJsonNode SerializeSeries(int  most_ancient_time,
148                                           int  most_recent_time,
149                                           unsigned long  tick_span,
150                                           const string &  name,
151                                           const string &  description) const;
152 
153     protected:
154         unique_ptr<TPSGTiming>      m_PSGTiming;
155 };
156 
157 
158 // LMDB cache operations are supposed to be fast.
159 // There are three tables covered by LMDB cache.
160 class CLmdbCacheTiming : public CPSGTimingBase
161 {
162     public:
163         CLmdbCacheTiming(unsigned long  min_stat_value,
164                          unsigned long  max_stat_value,
165                          unsigned long  n_bins,
166                          TOnePSGTiming::EScaleType  stat_type,
167                          bool &  reset_to_default);
168 };
169 
170 
171 // LMDB resolution may involve many tries with the cached tables.
172 class CLmdbResolutionTiming : public CPSGTimingBase
173 {
174     public:
175         CLmdbResolutionTiming(unsigned long  min_stat_value,
176                               unsigned long  max_stat_value,
177                               unsigned long  n_bins,
178                               TOnePSGTiming::EScaleType  stat_type,
179                               bool &  reset_to_default);
180 };
181 
182 
183 
184 // Cassandra operations are supposed to be slower than LMDB.
185 // There are three cassandra tables
186 class CCassTiming : public CPSGTimingBase
187 {
188     public:
189         CCassTiming(unsigned long  min_stat_value,
190                     unsigned long  max_stat_value,
191                     unsigned long  n_bins,
192                     TOnePSGTiming::EScaleType  stat_type,
193                     bool &  reset_to_default);
194 };
195 
196 
197 // Cassandra resolution may need a few tries with a two tables
198 class CCassResolutionTiming : public CPSGTimingBase
199 {
200     public:
201         CCassResolutionTiming(unsigned long  min_stat_value,
202                               unsigned long  max_stat_value,
203                               unsigned long  n_bins,
204                               TOnePSGTiming::EScaleType  stat_type,
205                               bool &  reset_to_default);
206 };
207 
208 
209 // Blob retrieval depends on a blob size
210 class CBlobRetrieveTiming : public CPSGTimingBase
211 {
212     public:
213         CBlobRetrieveTiming(unsigned long  min_blob_size,
214                             unsigned long  max_blob_size,
215                             unsigned long  min_stat_value,
216                             unsigned long  max_stat_value,
217                             unsigned long  n_bins,
218                             TOnePSGTiming::EScaleType  stat_type,
219                             bool &  reset_to_default);
~CBlobRetrieveTiming()220         ~CBlobRetrieveTiming() {}
221 
222     public:
223         virtual CJsonNode SerializeCombined(int  most_ancient_time,
224                                             int  most_recent_time,
225                                             unsigned long  tick_span,
226                                             const string &  name,
227                                             const string &  description) const;
228         virtual CJsonNode SerializeSeries(int  most_ancient_time,
229                                           int  most_recent_time,
230                                           unsigned long  tick_span,
231                                           const string &  name,
232                                           const string &  description) const;
233 
GetMinBlobSize(void) const234         unsigned long GetMinBlobSize(void) const
235         { return m_MinBlobSize; }
236 
GetMaxBlobSize(void) const237         unsigned long GetMaxBlobSize(void) const
238         { return m_MaxBlobSize; }
239 
240     private:
241         unsigned long      m_MinBlobSize;
242         unsigned long      m_MaxBlobSize;
243 };
244 
245 
246 // Out of range blob size; should not really happened
247 class CHugeBlobRetrieveTiming : public CPSGTimingBase
248 {
249     public:
250         CHugeBlobRetrieveTiming(unsigned long  min_stat_value,
251                                 unsigned long  max_stat_value,
252                                 unsigned long  n_bins,
253                                 TOnePSGTiming::EScaleType  stat_type,
254                                 bool &  reset_to_default);
255 };
256 
257 
258 // Not found blob
259 class CNotFoundBlobRetrieveTiming : public CPSGTimingBase
260 {
261     public:
262         CNotFoundBlobRetrieveTiming(unsigned long  min_stat_value,
263                                     unsigned long  max_stat_value,
264                                     unsigned long  n_bins,
265                                     TOnePSGTiming::EScaleType  stat_type,
266                                     bool &  reset_to_default);
267 };
268 
269 
270 // Named annotation retrieval
271 class CNARetrieveTiming : public CPSGTimingBase
272 {
273     public:
274         CNARetrieveTiming(unsigned long  min_stat_value,
275                           unsigned long  max_stat_value,
276                           unsigned long  n_bins,
277                           TOnePSGTiming::EScaleType  stat_type,
278                           bool &  reset_to_default);
279 };
280 
281 
282 // Split history retrieval
283 class CSplitHistoryRetrieveTiming : public CPSGTimingBase
284 {
285     public:
286         CSplitHistoryRetrieveTiming(unsigned long  min_stat_value,
287                                     unsigned long  max_stat_value,
288                                     unsigned long  n_bins,
289                                     TOnePSGTiming::EScaleType  stat_type,
290                                     bool &  reset_to_default);
291 };
292 
293 
294 // Public comment retrieval
295 class CPublicCommentRetrieveTiming : public CPSGTimingBase
296 {
297     public:
298         CPublicCommentRetrieveTiming(unsigned long  min_stat_value,
299                                      unsigned long  max_stat_value,
300                                      unsigned long  n_bins,
301                                      TOnePSGTiming::EScaleType  stat_type,
302                                      bool &  reset_to_default);
303 };
304 
305 
306 // Resolution
307 class CResolutionTiming : public CPSGTimingBase
308 {
309     public:
310         CResolutionTiming(unsigned long  min_stat_value,
311                           unsigned long  max_stat_value,
312                           unsigned long  n_bins,
313                           TOnePSGTiming::EScaleType  stat_type,
314                           bool &  reset_to_default);
315 };
316 
317 
318 class COperationTiming
319 {
320     public:
321         COperationTiming(unsigned long  min_stat_value,
322                          unsigned long  max_stat_value,
323                          unsigned long  n_bins,
324                          const string &  stat_type,
325                          unsigned long  small_blob_size);
~COperationTiming()326         ~COperationTiming() {}
327 
328     public:
329         // Blob size is taken into consideration only if
330         // operation == eBlobRetrieve
331         void Register(EPSGOperation  operation,
332                       EPSGOperationStatus  status,
333                       const TPSGS_HighResolutionTimePoint &  op_begin_ts,
334                       size_t  blob_size=0);
335 
336     public:
337         void Rotate(void);
338         void Reset(void);
339         CJsonNode Serialize(int  most_ancient_time,
340                             int  most_recent_time,
341                             const vector<CTempString> &  histogram_names,
342                             unsigned long  tick_span) const;
343 
344     private:
345         bool x_SetupBlobSizeBins(unsigned long  min_stat_value,
346                                  unsigned long  max_stat_value,
347                                  unsigned long  n_bins,
348                                  TOnePSGTiming::EScaleType  stat_type,
349                                  unsigned long  small_blob_size);
350         ssize_t x_GetBlobRetrievalBinIndex(unsigned long  blob_size);
351 
352     private:
353         // Note: 2 items, found and not found
354         vector<unique_ptr<CLmdbCacheTiming>>                m_LookupLmdbSi2csiTiming;
355         vector<unique_ptr<CLmdbCacheTiming>>                m_LookupLmdbBioseqInfoTiming;
356         vector<unique_ptr<CLmdbCacheTiming>>                m_LookupLmdbBlobPropTiming;
357         vector<unique_ptr<CCassTiming>>                     m_LookupCassSi2csiTiming;
358         vector<unique_ptr<CCassTiming>>                     m_LookupCassBioseqInfoTiming;
359         vector<unique_ptr<CCassTiming>>                     m_LookupCassBlobPropTiming;
360 
361         vector<unique_ptr<CLmdbResolutionTiming>>           m_ResolutionLmdbTiming;
362         vector<unique_ptr<CCassResolutionTiming>>           m_ResolutionCassTiming;
363 
364         vector<unique_ptr<CNARetrieveTiming>>               m_NARetrieveTiming;
365         vector<unique_ptr<CSplitHistoryRetrieveTiming>>     m_SplitHistoryRetrieveTiming;
366         vector<unique_ptr<CPublicCommentRetrieveTiming>>    m_PublicCommentRetrieveTiming;
367 
368         // The index is calculated basing on the blob size
369         vector<unique_ptr<CBlobRetrieveTiming>>             m_BlobRetrieveTiming;
370         vector<unsigned long>                               m_Ends;
371         unique_ptr<CHugeBlobRetrieveTiming>                 m_HugeBlobRetrievalTiming;
372         unique_ptr<CNotFoundBlobRetrieveTiming>             m_NotFoundBlobRetrievalTiming;
373         vector<uint64_t>                                    m_BlobByteCounters;
374         uint64_t                                            m_HugeBlobByteCounter;
375 
376         // Resolution timing
377         unique_ptr<CResolutionTiming>                       m_ResolutionErrorTiming;
378         unique_ptr<CResolutionTiming>                       m_ResolutionNotFoundTiming;
379         unique_ptr<CResolutionTiming>                       m_ResolutionFoundTiming;
380 
381         // 1, 2, 3, 4, 5+ trips to cassandra
382         vector<unique_ptr<CResolutionTiming>>               m_ResolutionFoundCassandraTiming;
383 
384         struct SInfo {
385             CPSGTimingBase *    m_Timing;
386             string              m_Name;
387             string              m_Description;
388 
389             // Specific for blob retrieval. They also have a cummulative
390             // counter for the blob bytes sent to the user.
391             uint64_t *          m_Counter;
392             string              m_CounterId;
393             string              m_CounterName;
394             string              m_CounterDescription;
395 
SInfoCOperationTiming::SInfo396             SInfo() :
397                 m_Timing(nullptr),
398                 m_Counter(nullptr)
399             {}
400 
SInfoCOperationTiming::SInfo401             SInfo(CPSGTimingBase *  timing,
402                   const string &  name, const string &  description) :
403                 m_Timing(timing), m_Name(name), m_Description(description),
404                 m_Counter(nullptr)
405             {}
406 
SInfoCOperationTiming::SInfo407             SInfo(CPSGTimingBase *  timing,
408                   const string &  name, const string &  description,
409                   uint64_t *  counter,
410                   const string &  counter_id,
411                   const string &  counter_name,
412                   const string &  counter_description) :
413                 m_Timing(timing), m_Name(name), m_Description(description),
414                 m_Counter(counter),
415                 m_CounterId(counter_id),
416                 m_CounterName(counter_name),
417                 m_CounterDescription(counter_description)
418             {}
419 
420             SInfo(const SInfo &) = default;
421             SInfo & operator=(const SInfo &) = default;
422             SInfo(SInfo &&) = default;
423             SInfo & operator=(SInfo &&) = default;
424         };
425         map<string, SInfo>                                  m_NamesMap;
426 
427         mutable mutex                                       m_Lock; // reset-rotate-serialize lock
428 };
429 
430 #endif /* PUBSEQ_GATEWAY_TIMING__HPP */
431 
432