1 /* 2 Copyright (c) 2005, 2021, Oracle and/or its affiliates. 3 All rights reserved. Use is subject to license terms. 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License, version 2.0, 7 as published by the Free Software Foundation. 8 9 This program is also distributed with certain software (including 10 but not limited to OpenSSL) that is licensed under separate terms, 11 as designated in a particular file or component or in included license 12 documentation. The authors of MySQL hereby grant you an additional 13 permission to link the program and your derivative works with the 14 separately licensed software that they have included with MySQL. 15 16 This program is distributed in the hope that it will be useful, 17 but WITHOUT ANY WARRANTY; without even the implied warranty of 18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 GNU General Public License, version 2.0, for more details. 20 21 You should have received a copy of the GNU General Public License 22 along with this program; if not, write to the Free Software 23 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 24 */ 25 26 #ifndef NdbIndexStat_H 27 #define NdbIndexStat_H 28 29 #include <ndb_types.h> 30 #include "NdbDictionary.hpp" 31 #include "NdbError.hpp" 32 #include "NdbIndexScanOperation.hpp" 33 class NdbIndexStatImpl; 34 35 /* 36 * Ordered index stats "v4". Includes 1) the old records_in_range in 37 * simplified form 2) the new scanned and stored stats. These are 38 * completely different. 1) makes a one-round-trip query directly to 39 * the index while 2) reads more extensive stats from sys tables where 40 * they were stored previously by NDB kernel. 41 * 42 * Methods in general return 0 on success and -1 on error. The error 43 * details are available via getNdbError(). 44 */ 45 46 class NdbIndexStat { 47 public: 48 NdbIndexStat(); 49 ~NdbIndexStat(); 50 51 // dummy defs to make handler compile at "ndb api" patch level alloc_cache(Uint32 entries)52 int alloc_cache(Uint32 entries) { return 0; } 53 enum { RR_UseDb = 1, RR_NoUpdate = 2 }; 54 55 /* 56 * Get latest error. Can be printed like any NdbError instance and 57 * includes some extras. 58 */ 59 struct Error : public NdbError { 60 int line; // source code line number 61 int extra; // extra error code 62 Error(); 63 }; 64 const Error& getNdbError() const; 65 66 /* 67 * Estimate how many records exist in given range. Does a single 68 * tree-dive on each index fragment, estimates the count from tree 69 * properties, and sums up the results. 70 * 71 * Caller provides index and scan transaction and range bounds. 72 * A scan operation is created and executed. The result is returned 73 * in out-parameter "count". The result is not transactional. Value 74 * zero is exact (range was empty when checked). 75 * 76 * This is basically a static method. The class instance is used only 77 * to return errors. 78 */ 79 int records_in_range(const NdbDictionary::Index* index, 80 NdbTransaction* trans, 81 const NdbRecord* key_record, 82 const NdbRecord* result_record, 83 const NdbIndexScanOperation::IndexBound* ib, 84 Uint64 table_rows, // not used 85 Uint64* count, 86 int flags); // not used 87 88 /* 89 * Methods for stored stats. 90 * 91 * There are two distinct users: 1) writer reads samples from sys 92 * tables and creates a new query cache 2) readers make concurrent 93 * stats queries on current query cache. 94 * 95 * Writer provides any Ndb object required. Its database name must be 96 * "mysql". No reference to it is kept. 97 * 98 * Readers provide structs such as Bound on stack or in TLS. The 99 * structs are opaque. With source code the structs can be cast to 100 * NdbIndexStatImpl structs. 101 */ 102 103 enum { 104 NoSysTables = 4714, // all sys tables missing 105 NoIndexStats = 4715, // given index has no stored stats 106 UsageError = 4716, // wrong state, invalid input 107 NoMemError = 4717, 108 InvalidCache = 4718, 109 InternalError = 4719, 110 BadSysTables = 4720, // sys tables partly missing or invalid 111 HaveSysTables = 4244, // create error if all sys tables exist 112 NoSysEvents = 4710, 113 BadSysEvents = BadSysTables, 114 HaveSysEvents = 746, 115 /* 116 * Following are for mysqld. Most are consumed by mysqld itself 117 * and should therefore not be seen by clients. 118 */ 119 MyNotAllow = 4721, // stats thread not open for requests 120 MyNotFound = 4722, // stats entry unexpectedly not found 121 MyHasError = 4723, // request ignored due to recent error 122 MyAbortReq = 4724, // request aborted by stats thread 123 AlienUpdate = 4725 // somebody else messed with stats 124 }; 125 126 /* 127 * Methods for sys tables. 128 * 129 * Create fails if any objects exist. Specific errors are 130 * BadSysTables (drop required) and HaveSysTables. 131 * 132 * Drop always succeeds and drops any objects that exist. 133 * 134 * Check succeeds if all correct objects exist. Specific errors are 135 * BadSysTables (drop required) and NoSysTables. 136 * 137 * Database of the Ndb object is used and must be "mysql" for kernel 138 * to see the tables. 139 */ 140 int create_systables(Ndb* ndb); 141 int drop_systables(Ndb* ndb); 142 int check_systables(Ndb* ndb); 143 144 /* 145 * Set index operated on. Allocates internal structs. Makes no 146 * database access and keeps no references to the objects. 147 */ 148 int set_index(const NdbDictionary::Index& index, 149 const NdbDictionary::Table& table); 150 151 /* 152 * Release index. Required only if re-used for another index. 153 */ 154 void reset_index(); 155 156 /* 157 * Trivial invocation of NdbDictionary::Dictionary::updateIndexStat. 158 */ 159 int update_stat(Ndb* ndb); 160 161 /* 162 * Trivial invocation of NdbDictionary::Dictionary::deleteIndexStat. 163 */ 164 int delete_stat(Ndb* ndb); 165 166 /* 167 * Cache types. 168 */ 169 enum CacheType { 170 CacheBuild = 1, // new cache under construction 171 CacheQuery = 2, // cache used to answer queries 172 CacheClean = 3 // old caches waiting to be deleted 173 }; 174 175 /* 176 * Move CacheQuery (if any) to CacheClean and CacheBuild (if any) to 177 * CacheQuery. The CacheQuery switch is atomic. 178 */ 179 void move_cache(); 180 181 /* 182 * Delete all CacheClean instances. This can be safely done after old 183 * cache queries have finished. Cache queries are fast since they do 184 * binary searches in memory. 185 */ 186 void clean_cache(); 187 188 /* 189 * Cache info. CacheClean may have several instances and the values 190 * for them are summed up. 191 */ 192 struct CacheInfo { 193 Uint32 m_count; // number of instances 194 Uint32 m_valid; // should be except for incomplete CacheBuild 195 Uint32 m_sampleCount; // number of samples 196 Uint32 m_totalBytes; // total bytes memory used 197 Uint64 m_save_time; // microseconds to read stats into cache 198 Uint64 m_sort_time; // microseconds to sort the cache 199 Uint32 m_ref_count; // in use by query_stat 200 // end v4 fields 201 }; 202 203 /* 204 * Get info about a cache type. 205 */ 206 void get_cache_info(CacheInfo& info, CacheType type) const; 207 208 /* 209 * Saved head record retrieved with get_head(). The database fields 210 * are updated by any method which reads stats tables. Stats exist if 211 * sampleVersion is not zero. 212 */ 213 struct Head { 214 Int32 m_found; // -1 no read done, 0 = no record, 1 = exists 215 Int32 m_eventType; // if polling, NdbDictionary::Event::TE_INSERT etc 216 Uint32 m_indexId; 217 Uint32 m_indexVersion; 218 Uint32 m_tableId; 219 Uint32 m_fragCount; 220 Uint32 m_valueFormat; 221 Uint32 m_sampleVersion; 222 Uint32 m_loadTime; 223 Uint32 m_sampleCount; 224 Uint32 m_keyBytes; 225 // end v4 fields 226 }; 227 228 /* 229 * Get latest saved head record. Makes no database access. 230 */ 231 void get_head(Head& head) const; 232 233 /* 234 * Read stats head record for the index. Returns error and sets code 235 * to NoIndexStats if head record does not exist or sample version is 236 * zero. Use get_head() to retrieve the results. 237 */ 238 int read_head(Ndb* ndb); 239 240 /* 241 * Read current version of stats into CacheBuild. A move_cache() is 242 * required before it is available for queries. 243 */ 244 int read_stat(Ndb* ndb); 245 246 /* 247 * Reader provides bounds for cache query. The struct must be 248 * initialized from a thread-local byte buffer of the given size. 249 * NdbIndexStat instance is used and must have index set. Note that 250 * a bound becomes low or high only as part of Range. 251 */ 252 enum { BoundBufferBytes = 8192 }; 253 struct Bound { 254 Bound(const NdbIndexStat* is, void* buffer); 255 void* m_impl; 256 }; 257 258 /* 259 * Add non-NULL attribute value to the bound. May return error for 260 * invalid data. 261 */ 262 int add_bound(Bound& bound, const void* value); 263 264 /* 265 * Add NULL attribute value to the bound. 266 */ 267 int add_bound_null(Bound& bound); 268 269 /* 270 * A non-empty bound must be set strict (true) or non-strict (false). 271 * For empty bound this must remain unset (-1). 272 */ 273 void set_bound_strict(Bound& bound, int strict); 274 275 /* 276 * To re-use same bound instance, a reset is required. 277 */ 278 void reset_bound(Bound& bound); 279 280 /* 281 * Queries take a range consisting of low and high bound (start key 282 * and end key in mysql). 283 */ 284 struct Range { 285 Range(Bound& bound1, Bound& bound2); 286 Bound& m_bound1; 287 Bound& m_bound2; 288 }; 289 290 /* 291 * After defining bounds, the range must be finalized. This updates 292 * internal info. Usage error is possible. 293 */ 294 int finalize_range(Range& range); 295 296 /* 297 * Reset the bounds. 298 */ 299 void reset_range(Range& range); 300 301 /* 302 * Convert NdbRecord index bound to Range. Invokes reset and finalize 303 * and cannot be mixed with the other methods. 304 */ 305 int convert_range(Range& range, 306 const NdbRecord* key_record, 307 const NdbIndexScanOperation::IndexBound* ib); 308 309 /* 310 * Reader provides storage for stats values. The struct must be 311 * initialized from a thread-local byte buffer of the given size. 312 */ 313 enum { StatBufferBytes = 2048 }; 314 struct Stat { 315 Stat(void* buffer); 316 void* m_impl; 317 }; 318 319 /* 320 * Compute Stat for a Range from the query cache. Returns error 321 * if there is no valid query cache. The Stat is used to get 322 * stats values without further reference to the Range. 323 */ 324 int query_stat(const Range& range, Stat& stat); 325 326 /* 327 * Check if range is empty i.e. bound1 >= bound2 (for bounds this 328 * means empty) or the query cache is empty. The RIR and RPK return 329 * 1.0 if range is empty. 330 */ 331 static void get_empty(const Stat& stat, bool* empty); 332 333 /* 334 * Get estimated RIR (records in range). Value is always >= 1.0 since 335 * no exact 0 rows can be returned. 336 */ 337 static void get_rir(const Stat& stat, double* rir); 338 339 /* 340 * Get estimated RPK (records per key) at given level k (from 0 to 341 * NK-1 where NK = number of index keys). Value is >= 1.0. 342 */ 343 static void get_rpk(const Stat& stat, Uint32 k, double* rpk); 344 345 /* 346 * Get a short string summarizing the rules used. 347 */ 348 enum { RuleBufferBytes = 80 }; 349 static void get_rule(const Stat& stat, char* buffer); 350 351 /* 352 * Events (there is 1) for polling. These are dictionary objects. 353 * Correct sys tables must exist. Drop ignores non-existing events. 354 */ 355 int create_sysevents(Ndb* ndb); 356 int drop_sysevents(Ndb* ndb); 357 int check_sysevents(Ndb* ndb); 358 359 /* 360 * Create listener for stats updates. Only 1 is allowed. 361 */ 362 int create_listener(Ndb* ndb); 363 364 /* 365 * Start listening for events (call NdbEventOperation::execute). 366 */ 367 int execute_listener(Ndb* ndb); 368 369 /* 370 * Poll the listener (call Ndb::pollEvents). Returns 1 if there are 371 * events available and 0 otherwise, or -1 on failure as usual. 372 */ 373 int poll_listener(Ndb* ndb, int max_wait_ms); 374 375 /* 376 * Get next available event. Returns 1 if a new event was returned 377 * and 0 otherwise, or -1 on failure as usual. Use get_heed() to 378 * retrieve event type and data. 379 */ 380 int next_listener(Ndb* ndb); 381 382 /* 383 * Drop the listener. 384 */ 385 int drop_listener(Ndb* ndb); 386 387 /* 388 * Memory allocator for stats cache data (key and value byte arrays). 389 * Implementation default uses malloc/free. The memory in use is the 390 * sum of CacheInfo::m_totalBytes from all cache types. 391 */ 392 struct Mem { 393 Mem(); 394 virtual ~Mem(); 395 virtual void* mem_alloc(UintPtr size) = 0; 396 virtual void mem_free(void* ptr) = 0; 397 }; 398 399 /* 400 * Set a non-default memory allocator. 401 */ 402 void set_mem_handler(Mem* mem); 403 404 // get impl class for use in NDB API programs 405 NdbIndexStatImpl& getImpl(); 406 407 private: 408 int addKeyPartInfo(const NdbRecord* record, 409 const char* keyRecordData, 410 Uint32 keyPartNum, 411 const NdbIndexScanOperation::BoundType boundType, 412 Uint32* keyStatData, 413 Uint32& keyLength); 414 415 // stored stats 416 417 friend class NdbIndexStatImpl; 418 NdbIndexStat(NdbIndexStatImpl& impl); 419 NdbIndexStatImpl& m_impl; 420 }; 421 422 class NdbOut& 423 operator<<(class NdbOut& out, const NdbIndexStat::Error&); 424 425 #endif 426