Skip to content
This repository was archived by the owner on Jun 2, 2021. It is now read-only.

Commit c50b3bc

Browse files
authored
Merge pull request #33 from tjstansell/fix-mysql-threading-connections
creation of db connection arguments was not thread-safe
2 parents e046e0f + 791b445 commit c50b3bc

File tree

1 file changed

+36
-8
lines changed

1 file changed

+36
-8
lines changed

newrelic_python_agent/plugins/mysql.py

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ def _errno(err):
146146
"gauge": {
147147
"status": [
148148
"innodb_page_size",
149+
"innodb_data_pending_fsyncs",
149150
"max_used_connections",
150151
"open_files",
151152
"open_streams",
@@ -352,7 +353,6 @@ def _errno(err):
352353
"innodb_buffer_pool_wait_free",
353354
"innodb_buffer_pool_write_requests",
354355
["innodb_data_fsyncs", "Fsyncs"],
355-
"innodb_data_pending_fsyncs",
356356
"innodb_data_read",
357357
"innodb_data_reads",
358358
"innodb_data_writes",
@@ -426,6 +426,25 @@ class MySQL(base.Plugin):
426426
has_slave_data = False
427427
raw_metrics = dict()
428428

429+
#
430+
# Track the server uuid just to verify who we are talking to.
431+
#
432+
# This was used to help debug some threading issues where connections were going
433+
# to the wrong database instance. Left it here, but unused, because it seemed useful.
434+
#
435+
def verify_uuid(self, cursor):
436+
cursor.execute("show variables where variable_name like 'server%'")
437+
server = self.parse_set_stats(cursor)
438+
if 'server_uuid' in self.derive_last_interval.keys():
439+
if server['server_uuid'] == self.derive_last_interval['server_uuid']:
440+
self.logger.debug("server_uuid matches %s", server['server_uuid'])
441+
else:
442+
raise ValueError("server_uuid (%s) does not match previous value of %s" %
443+
(server['server_uuid'], self.derive_last_interval['server_uuid']))
444+
else:
445+
self.derive_last_interval['server_uuid'] = server['server_uuid']
446+
self.logger.info("server_uuid is %s", server['server_uuid'])
447+
429448
def collect_stats(self, cursor):
430449
"""
431450
Loop through each of the requested metrics and collect the data.
@@ -761,7 +780,7 @@ def is_number(self, value):
761780
:result: True if the value is a number type, False otherwise
762781
:rtype: bool
763782
"""
764-
if isinstance(value, (int, float, long, complex)):
783+
if isinstance(value, (int, float, long, complex)): # noqa
765784
return True
766785
return False
767786

@@ -864,7 +883,9 @@ def connect(self):
864883
865884
"""
866885

886+
self.logger.debug("creating DB connection")
867887
conn = sql.connect(**self.connection_arguments)
888+
self.logger.debug("DB connection ready: %r", conn.get_host_info())
868889
return conn
869890

870891
@property
@@ -875,7 +896,10 @@ def connection_arguments(self):
875896
via double-splat
876897
"""
877898
filtered_args = ['name', 'metrics']
878-
args = DEFAULT_CONNECT_ARGS
899+
900+
# make sure we make a copy of this global so it is thread-safe
901+
args = dict(DEFAULT_CONNECT_ARGS)
902+
879903
for key in set(self.config) - set(filtered_args):
880904
if key == 'dbname':
881905
args['database'] = self.config[key]
@@ -890,12 +914,16 @@ def poll(self):
890914
self.initialize()
891915
self.raw_metrics = dict()
892916
try:
893-
self.connection = self.connect()
894-
cursor = self.connection.cursor()
895-
self.collect_stats(cursor)
896-
cursor.close()
897-
self.connection.close()
917+
# open a new connection
918+
with self.connect() as cursor:
919+
# self.verify_uuid(cursor)
920+
# self.logger.debug("done verifying uuid")
921+
self.collect_stats(cursor)
922+
self.logger.debug("done collecting data")
923+
# build stats
898924
self.add_stats()
925+
except ValueError as err:
926+
self.logger.exception(err)
899927
except sql.Error as err:
900928
if _errno(err) == ER_ACCESS_DENIED_ERROR:
901929
self.logger.error("Something is wrong with your user name or password")

0 commit comments

Comments
 (0)