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