81
81
SQL_REPLICATION_ROLE_AWS_AURORA ,
82
82
SQL_SERVER_ID_AWS_AURORA ,
83
83
SQL_SERVER_UUID ,
84
+ SQL_TIDB_VERSION ,
84
85
show_replica_status_query ,
85
86
)
86
87
from .statement_samples import MySQLStatementSamples
87
88
from .statements import MySQLStatementMetrics
89
+ from .tidb_slow_query import TiDBSlowQueryMonitor
88
90
from .util import DatabaseConfigurationError , connect_with_session_variables # noqa: F401
89
91
from .version_utils import get_version
90
92
@@ -121,6 +123,7 @@ def __init__(self, name, init_config, instances):
121
123
self ._agent_hostname = None
122
124
self ._database_hostname = None
123
125
self ._is_aurora = None
126
+ self ._is_tidb = None
124
127
self ._performance_schema_enabled = None
125
128
self ._events_wait_current_enabled = None
126
129
self ._group_replication_active = None
@@ -146,6 +149,7 @@ def __init__(self, name, init_config, instances):
146
149
self ._mysql_metadata = MySQLMetadata (self , self ._config , self ._get_connection_args ())
147
150
self ._query_activity = MySQLActivity (self , self ._config , self ._get_connection_args ())
148
151
self ._index_metrics = MySqlIndexMetrics (self ._config )
152
+ self ._tidb_slow_query_monitor = None # Initialized later if TiDB is detected
149
153
# _database_instance_emitted: limit the collection and transmission of the database instance metadata
150
154
self ._database_instance_emitted = TTLCache (
151
155
maxsize = 1 ,
@@ -288,6 +292,7 @@ def set_resource_tags(self):
288
292
def set_version (self , db ):
289
293
self .version = get_version (db )
290
294
self .is_mariadb = self .version .flavor == "MariaDB"
295
+ self .is_tidb = self .version .flavor == "TiDB"
291
296
self .tag_manager .set_tag ("dbms_flavor" , self .version .flavor .lower (), replace = True )
292
297
293
298
def set_server_uuid (self , db ):
@@ -314,6 +319,12 @@ def _check_database_configuration(self, db):
314
319
self ._is_group_replication_active (db )
315
320
316
321
def _check_performance_schema_enabled (self , db ):
322
+ # TiDB doesn't have performance_schema but uses information_schema.cluster_statements_summary instead
323
+ if self ._get_is_tidb (db ):
324
+ self ._performance_schema_enabled = False
325
+ self .log .debug ("TiDB detected, performance_schema check skipped" )
326
+ return self ._performance_schema_enabled
327
+
317
328
with closing (db .cursor (CommenterCursor )) as cursor :
318
329
cursor .execute ("SHOW VARIABLES LIKE 'performance_schema'" )
319
330
results = dict (cursor .fetchall ())
@@ -420,6 +431,15 @@ def check(self, _):
420
431
self ._query_activity .run_job_loop (dbm_tags )
421
432
self ._mysql_metadata .run_job_loop (dbm_tags )
422
433
434
+ # Initialize and run TiDB slow query monitor if this is TiDB
435
+ if self ._get_is_tidb (db ) and self ._tidb_slow_query_monitor is None :
436
+ self ._tidb_slow_query_monitor = TiDBSlowQueryMonitor (
437
+ self , self ._config , self ._get_connection_args ()
438
+ )
439
+
440
+ if self ._tidb_slow_query_monitor :
441
+ self ._tidb_slow_query_monitor .run_job_loop (dbm_tags )
442
+
423
443
# keeping track of these:
424
444
self ._put_qcache_stats ()
425
445
@@ -438,6 +458,8 @@ def cancel(self):
438
458
self ._statement_metrics .cancel ()
439
459
self ._query_activity .cancel ()
440
460
self ._mysql_metadata .cancel ()
461
+ if self ._tidb_slow_query_monitor :
462
+ self ._tidb_slow_query_monitor .cancel ()
441
463
442
464
def _new_query_executor (self , queries ):
443
465
return QueryExecutor (
@@ -462,7 +484,8 @@ def _get_runtime_queries(self, db):
462
484
463
485
if self .performance_schema_enabled :
464
486
queries .extend ([QUERY_USER_CONNECTIONS ])
465
- if self ._index_metrics .include_index_metrics :
487
+ # TiDB doesn't have mysql.innodb_index_stats table
488
+ if self ._index_metrics .include_index_metrics and not self ._get_is_tidb (db ):
466
489
queries .extend (self ._index_metrics .queries )
467
490
self ._runtime_queries_cached = self ._new_query_executor (queries )
468
491
self ._runtime_queries_cached .compile_queries ()
@@ -582,7 +605,11 @@ def _collect_metrics(self, db, tags):
582
605
# Innodb metrics are not available for Aurora reader instances
583
606
if self ._is_aurora and self ._replication_role == "reader" :
584
607
self .log .debug ("Skipping innodb metrics collection for reader instance" )
608
+ # TiDB does not support InnoDB storage engine
609
+ elif self ._get_is_tidb (db ):
610
+ self .log .info ("Skipping innodb metrics collection for TiDB instance" )
585
611
else :
612
+ self .log .debug ("Collecting InnoDB metrics (not Aurora reader, not TiDB)" )
586
613
with tracked_query (self , operation = "innodb_metrics" ):
587
614
results .update (self .innodb_stats .get_stats_from_innodb_status (db ))
588
615
self .innodb_stats .process_innodb_stats (results , self ._config .options , metrics )
@@ -593,23 +620,29 @@ def _collect_metrics(self, db, tags):
593
620
results ['Binlog_space_usage_bytes' ] = self ._get_binary_log_stats (db )
594
621
595
622
# Compute key cache utilization metric
596
- key_blocks_unused = collect_scalar ('Key_blocks_unused' , results )
597
- key_cache_block_size = collect_scalar ('key_cache_block_size' , results )
598
- key_buffer_size = collect_scalar ('key_buffer_size' , results )
599
- results ['Key_buffer_size' ] = key_buffer_size
623
+ # TiDB doesn't have MyISAM key cache metrics
624
+ if not self ._get_is_tidb (db ):
625
+ key_blocks_unused = collect_scalar ('Key_blocks_unused' , results )
626
+ key_cache_block_size = collect_scalar ('key_cache_block_size' , results )
627
+ key_buffer_size = collect_scalar ('key_buffer_size' , results )
628
+ results ['Key_buffer_size' ] = key_buffer_size
600
629
601
- try :
602
- # can be null if the unit is missing in the user config (4 instead of 4G for eg.)
603
- if key_buffer_size != 0 :
604
- key_cache_utilization = 1 - ((key_blocks_unused * key_cache_block_size ) / key_buffer_size )
605
- results ['Key_cache_utilization' ] = key_cache_utilization
606
-
607
- results ['Key_buffer_bytes_used' ] = collect_scalar ('Key_blocks_used' , results ) * key_cache_block_size
608
- results ['Key_buffer_bytes_unflushed' ] = (
609
- collect_scalar ('Key_blocks_not_flushed' , results ) * key_cache_block_size
610
- )
611
- except TypeError as e :
612
- self .log .error ("Not all Key metrics are available, unable to compute: %s" , e )
630
+ try :
631
+ # can be null if the unit is missing in the user config (4 instead of 4G for eg.)
632
+ if key_buffer_size != 0 and key_cache_block_size is not None and key_blocks_unused is not None :
633
+ key_cache_utilization = 1 - ((key_blocks_unused * key_cache_block_size ) / key_buffer_size )
634
+ results ['Key_cache_utilization' ] = key_cache_utilization
635
+
636
+ if key_cache_block_size is not None :
637
+ key_blocks_used = collect_scalar ('Key_blocks_used' , results )
638
+ key_blocks_not_flushed = collect_scalar ('Key_blocks_not_flushed' , results )
639
+
640
+ if key_blocks_used is not None :
641
+ results ['Key_buffer_bytes_used' ] = key_blocks_used * key_cache_block_size
642
+ if key_blocks_not_flushed is not None :
643
+ results ['Key_buffer_bytes_unflushed' ] = key_blocks_not_flushed * key_cache_block_size
644
+ except (TypeError , ZeroDivisionError ) as e :
645
+ self .log .error ("Not all Key metrics are available, unable to compute: %s" , e )
613
646
614
647
metrics .update (VARIABLES_VARS )
615
648
metrics .update (INNODB_VARS )
@@ -681,7 +714,10 @@ def _collect_metrics(self, db, tags):
681
714
metrics .update (TABLE_VARS )
682
715
683
716
if self ._config .replication_enabled :
684
- if self .performance_schema_enabled and self ._group_replication_active :
717
+ # TiDB does not support MySQL replication
718
+ if self ._get_is_tidb (db ):
719
+ self .log .debug ("Skipping replication metrics collection for TiDB instance" )
720
+ elif self .performance_schema_enabled and self ._group_replication_active :
685
721
self .log .debug ('Collecting group replication metrics.' )
686
722
with tracked_query (self , operation = "group_replication_metrics" ):
687
723
self ._collect_group_replica_metrics (db , results )
@@ -1126,6 +1162,35 @@ def _get_is_aurora(self, db):
1126
1162
1127
1163
return self ._is_aurora
1128
1164
1165
+ def _get_is_tidb (self , db ):
1166
+ """
1167
+ Tests if the instance is a TiDB database and caches the result.
1168
+ """
1169
+ if self ._is_tidb is not None :
1170
+ return self ._is_tidb
1171
+
1172
+ try :
1173
+ with closing (db .cursor (CommenterCursor )) as cursor :
1174
+ cursor .execute (SQL_TIDB_VERSION )
1175
+ result = cursor .fetchone ()
1176
+ self .log .debug ("TiDB detection - VERSION() result: %s" , result )
1177
+ if result and 'TiDB' in result [0 ]:
1178
+ self ._is_tidb = True
1179
+ self .log .info ("Detected TiDB instance" )
1180
+ else :
1181
+ self ._is_tidb = False
1182
+
1183
+ except Exception :
1184
+ self .warning (
1185
+ "Unable to determine if server is TiDB. If this is a TiDB database, some "
1186
+ "information may be unavailable: %s" ,
1187
+ traceback .format_exc (),
1188
+ )
1189
+ self ._is_tidb = False
1190
+ return False
1191
+
1192
+ return self ._is_tidb
1193
+
1129
1194
@classmethod
1130
1195
def _get_stats_from_status (cls , db ):
1131
1196
with closing (db .cursor (CommenterCursor )) as cursor :
@@ -1164,6 +1229,12 @@ def _check_innodb_engine_enabled(self, db):
1164
1229
# table. Later is chosen because that involves no string parsing.
1165
1230
if self ._is_innodb_engine_enabled_cached is not None :
1166
1231
return self ._is_innodb_engine_enabled_cached
1232
+
1233
+ # TiDB doesn't have InnoDB engine
1234
+ if self ._get_is_tidb (db ):
1235
+ self ._is_innodb_engine_enabled_cached = False
1236
+ return self ._is_innodb_engine_enabled_cached
1237
+
1167
1238
try :
1168
1239
with closing (db .cursor (CommenterCursor )) as cursor :
1169
1240
cursor .execute (SQL_INNODB_ENGINES )
@@ -1439,13 +1510,16 @@ def _report_warnings(self):
1439
1510
1440
1511
def _send_database_instance_metadata (self ):
1441
1512
if self .database_identifier not in self ._database_instance_emitted :
1513
+ # Keep dbms as "mysql" for all MySQL-compatible databases
1514
+ # The specific flavor is identified through the dbms_flavor tag
1515
+ dbms = "mysql"
1442
1516
event = {
1443
1517
"host" : self .reported_hostname ,
1444
1518
"port" : self ._config .port ,
1445
1519
"database_instance" : self .database_identifier ,
1446
1520
"database_hostname" : self .database_hostname ,
1447
1521
"agent_version" : datadog_agent .get_version (),
1448
- "dbms" : "mysql" ,
1522
+ "dbms" : dbms ,
1449
1523
"kind" : "database_instance" ,
1450
1524
"collection_interval" : self ._config .database_instance_collection_interval ,
1451
1525
'dbms_version' : self .version .version + '+' + self .version .build ,
@@ -1471,6 +1545,9 @@ def set_cluster_tags(self, db):
1471
1545
if self ._group_replication_active :
1472
1546
self .log .debug ("Group replication cluster tags are not currently supported" )
1473
1547
return
1548
+ if self ._get_is_tidb (db ):
1549
+ self .log .debug ("TiDB cluster tags are not currently supported" )
1550
+ return
1474
1551
1475
1552
replica_status = self ._get_replica_replication_status (db )
1476
1553
if replica_status :
0 commit comments