-
Notifications
You must be signed in to change notification settings - Fork 403
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pymongo trace tcp connections #1455
Pymongo trace tcp connections #1455
Conversation
8867e90
to
8a02aa3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great after a first pass! Just need a few tests to confirm.
To address your questions from the issue thread:
What could be a good approach to test the new trace?
What we typically do is we replace the default tracer instance with our own dummy tracer which will capture all the spans so that we can access them for testing. Looking at the pymongo test suite it's a bit of a mess... I'd recommend to create a separate test case class in tests/contrib/pymongo/test.py
starting with something like:
class TestPymongoPool(BaseTracerTestCase):
def setUp(self):
patch()
self.tracer = get_dummy_tracer()
Pin.override(pymongo.pool.Pool, tracer=self.tracer)
def tearDown(self):
unpatch()
def test_pool(self):
# do a pool operation which will generate traces
spans = self.tracer.writer.pop()
# assert on the spans
Are the tags correct?
They look good to me after a first pass. Will look closer on the next pass :)
Is the way I instanciate the pin correct to?
Yep this looks good to me!
To make the old tests pass, I'm considering dropping from the spans retrieved by the writer all "tcp connect" spans. That would mean, for example, adding a list comprehension like spans = [span for span in spans if span.name in ("mongo_op", "pymongo.cmd")] after this line. Do you think it is a good idea? If not, can you recommend me another approach?
Hmm yeah I was thinking about that as I was going through the test suite. Yes, I think this is a good approach as any to get around the design of the current tests. (As an aside we should certainly rewrite the test suite for pymongo as part of the rewrite 😛)
c2fd372
to
25884ec
Compare
Hey! Sorry I have not taken the time lately to continue working on that PR. Thanks a lot for your review @Kyle-Verhoog, it has been very helpful. The PR is pretty much ready, I've just been stuck on a problem. I'm leaving a comment on the PR. If you could help me once again, that would be greatly appreciated! |
ddtrace/contrib/pymongo/patch.py
Outdated
|
||
def traced_pool_connect(wrapped, instance, args, kwargs): | ||
pin = Pin._find(wrapped, instance) | ||
is_monitoring_pool = instance.opts.connect_timeout == instance.opts.socket_timeout |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have discovered that pymongo maintains a background monitoring pool.
The thing is that since we patch a function in the pool class, we get traces from both the monitoring pool (created here) and the active pool (created here) while we only want to retrieve traces from the active pool.
I think we can workaround the problem by performing a check on the instance. For the sake of the example, I've used an implementation detail on the timeout of the pool sockets to detect which pool emitted the span but that is definitely not satisfying.
Do you have an idea to detect which pool is emitting the span? Would patching pymongo's _create_pool_for_server
and _create_pool_for_monitor
to add an hidden attribute to the pool be an acceptable solution?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey! Sorry for the long delay. Hmmm interesting. After looking at the examples (thanks for the code references!) it does seem like patching _create_pool_for_server
would make sense. IMO it'd be better if we're able to avoid patching the undesired path entirely. Is it relatively easy to accomplish?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries :) Once again, thanks for your time @Kyle-Verhoog
IMO it'd be better if we're able to avoid patching the undesired path entirely. Is it relatively easy to accomplish?
Agree with you but tbh I have not found a way to do it :/ Basically in pseudo-python, the flow is like this:
class PyMongoClient:
def __init__(self, ip, port):
self.ip = ip
self.port = port
# This runs in a separate thread
self.start_monitoring()
def start_monitoring(self):
while True:
for monitor in self.monitors:
monitor.get_socket(self.ip, self.port) as sock_info:
sock_info.health_check()
time.sleep(x)
def run_cmd(self, cmd):
server = self.servers[(self.ip, self.port)]
with server.get_socket(self.ip, self.port) as sock_info:
sock_info.send(cmd)
class Server:
def get_socket(self):
return self.pool.get_socket()
class Monitor:
def get_socket(self);
return self.pool.get_socket()
class Pool:
def get_socket(self):
# (...)
try:
# Lookup of an already existing, ready socket. This call does not take time
socket = self.sockets.popleft()
except IndexError:
# Creation of a new socket+handshake. This call does take time
socket = self.connect()
return socket
So basically:
- If we patch
pymongo.server.Server
'sget_socket
, we actually trace bothPool
'sself.sockets.popleft()
andPool
'sself.connect()
. The second one is effectively what we are trying to trace while the first one is not a network call but a lookup in memory which we do not want to trace (I think?) - If we patch
pymongo.pool.Pool
'sself.connect()
, we only trace actual network calls but we trace network calls of both the Monitor object and the Server object. That means that we generate traces for both the pymongo thread that handle incoming requests (good) but also from the pymongo thread that performs monitoring health_checks on the remote address (bad)
I do not see a way to only patch the critical path, unfortunately :( That leaves us with 2 viable options imo:
- Trace
pymongo.pool.Pool
'sget_socket
: it will generate tiny traces when the socket already exists and classic traces when the socket needs to be created - Do the hack which adds an hidden attribute pool instances that belong to
Servers
: it will generate traces only for actual network calls linked to pymongo direct requests but well, it's a bit of a hack
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for that write-up! It's actually really helpful as I'd forgotten some of the context 🙂.
So I'm okay with patching get_socket
in general even if there's the chance that it's a quick memory look up. It's meaningful sometimes which is enough reason IMO and our overhead shouldn't be too bad (unless this is done a crazy amount of times). I think we should avoid instrumenting the health checks from Monitor.
Trace
pymongo.pool.Pool
'sget_socket
: it will generate tiny traces when the socket already exists and classic traces when the socket needs to be created
Did you mean Server.get_socket
here? Else we'd still be getting the health checks? If you did mean Server.get_socket
I think that sounds like the way to go 👍. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean Server.get_socket here?
Yep my bad, messed things up! Alright I'm changing the code to patch Server.get_socket
:). I'll update you when it's done!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha no problem. And great! I'm excited to get this merged 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm I've ran into a problem implementing that. Actually Pool
's get_socket
's code creates&connects the socket and then yields it, making Server
's get_socket
's a generator. Thus, we cannot trace it effectively with wrap_function_wrapper
since generator functions instantly return a generator and the actual code is executed when the parent function decides to consume it.
I believe a workaround could be to consume the socket from the generator in our patch and then yield the socket but I don't think it is a good idea. Are there some wrappers to work with generators in ddtrace?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agh, I see. Hmm we don't have any utils for patching generators unfortunately. I think we're okay to intercept the generator and yield the socket ourselves as you described. We just unfortunately can't make use of yield from
to maintain compatibility with Python 2.
What are your thoughts?
d921dd6
to
e705192
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice 👍! I think once the monitor pool issue is figured out I think we're good to go 🙂
49a3b73
to
73b9674
Compare
Hey there @Kyle-Verhoog ! I've finally taken the time to finish this PR, sorry for the delay! Do you mind having a final look? Also, it looks like |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sweet! I think it's ready in terms of the integration. I think it'd be good to have a few tests (we can use the ones you've already written) checking that the functionality of pymongo is maintained.
Also feel free to format the files with black
since we're starting to do that across the project 😄
73b9674
to
89ae97e
Compare
Regarding black, I guess I'm lucky that nothing has to be done (I think? I'm not 100% sure about the cmds I used)
|
@LetzNico oh right. pymongo is probably on the ignore list in |
a5ee81d
to
a2ade67
Compare
It seems more logical to put it in the patch file rather than in the client file
* Closes DataDog#1442. This should trace all calls to Server.get_socket(). This is useful because for each pymongo cmd, the lib first retrieves a connected socket and then executes the cmd. Getting a connected socket takes time when it is not picked from the cache, hence it should be a good idea to trace that operation. Note that this change does not apply to the soon-to-be deprecated `trace_mongo_client`. * Introduces module patching (using ddtrace.vendor.wrapt) to pymongo contrib. In time, the old TracedMongoClient should be replaced by module patchs only.
a2ade67
to
e0a35b3
Compare
Hey there @Kyle-Verhoog ! I finally took some time to wrap up this PR. Excited to hear back from you and hope it will get merged! 🙌 |
hey @LetzNico! Thanks for getting back around to it! I was out on vacation last week but definitely gonna take a look at this, this week once I get caught back up 😄 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉 @LetzNico thanks for seeing this to the finish line!! 😄
Yeeey! Thanks for your time! Please hit me up in the future for support regarding this feature or if I can be of any help on related topics :) |
* pymongo: move old trace_mongo_client to patch.py It seems more logical to put it in the patch file rather than in the client file * pymongo: trace Server.get_socket() * Closes #1442. This should trace all calls to Server.get_socket(). This is useful because for each pymongo cmd, the lib first retrieves a connected socket and then executes the cmd. Getting a connected socket takes time when it is not picked from the cache, hence it should be a good idea to trace that operation. Note that this change does not apply to the soon-to-be deprecated `trace_mongo_client`. * Introduces module patching (using ddtrace.vendor.wrapt) to pymongo contrib. In time, the old TracedMongoClient should be replaced by module patchs only. * pymongo: reference #1501 Co-authored-by: Kyle Verhoog <kyle@verhoog.ca>
This fix introduces a few changes to improve overall CircleCI job runtime. **Before:** - Runtime: 28+ minutes - Credits: 33k **After:** - Runtime: ~22 minutes (-20%) - Credits: ~15k (-55%) ### Fix mysql test pattern The `mysql` CircleCI job was using the pattern "mysql", which would also match the `mysqldb` jobs. This change updates the test pattern for all mysql jobs to include an ending `$`. <details> <summary>Before</summary> ``` ❯ riot list mysql No. Hash Name Interpreter Environment Packages #554 1a1c1fa mysql Interpreter(_hint='2.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #555 8ba1245 mysql Interpreter(_hint='2.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.23' #556 1e72d9c mysql Interpreter(_hint='3.5') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #557 16025dc mysql Interpreter(_hint='3.5') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.23' #558 1cdb1c5 mysql Interpreter(_hint='3.6') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #559 1230ff8 mysql Interpreter(_hint='3.6') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.29' #560 729b5a8 mysql Interpreter(_hint='3.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #561 1133f63 mysql Interpreter(_hint='3.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' #562 129b0f2 mysql Interpreter(_hint='3.8') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #563 12db871 mysql Interpreter(_hint='3.8') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' #564 270f95a mysql Interpreter(_hint='3.9') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #565 9182ae0 mysql Interpreter(_hint='3.9') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' #566 137147e mysql Interpreter(_hint='3.10') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python~=8.0.28' #567 f26af3b mysql Interpreter(_hint='3.10') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' #568 2eb00ea mysql Interpreter(_hint='3.11') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python~=8.0.31' #569 10d623a mysql Interpreter(_hint='3.11') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' #1450 9be82d5 mysqldb Interpreter(_hint='2.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=1.4.6' #1451 17a3e91 mysqldb Interpreter(_hint='3.5') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=1.4.6' #1452 1c338bf mysqldb Interpreter(_hint='3.6') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.0' #1453 162a527 mysqldb Interpreter(_hint='3.6') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.1' #1454 19ef42f mysqldb Interpreter(_hint='3.6') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient' #1455 145f708 mysqldb Interpreter(_hint='3.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.0' #1456 73a2d0b mysqldb Interpreter(_hint='3.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.1' #1457 11b2766 mysqldb Interpreter(_hint='3.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient' #1458 1fcb3db mysqldb Interpreter(_hint='3.8') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.0' #1459 1c5e6ed mysqldb Interpreter(_hint='3.8') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.1' #1460 137f08a mysqldb Interpreter(_hint='3.8') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient' #1461 149f779 mysqldb Interpreter(_hint='3.9') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.0' #1462 14a543a mysqldb Interpreter(_hint='3.9') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.1' #1463 1eea232 mysqldb Interpreter(_hint='3.9') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient' #1464 14a543a mysqldb Interpreter(_hint='3.9') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.1' #1465 1eea232 mysqldb Interpreter(_hint='3.9') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient' #1466 10bc6fe mysqldb Interpreter(_hint='3.10') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.1' #1467 bf00ea5 mysqldb Interpreter(_hint='3.10') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient' #1468 186525e mysqldb Interpreter(_hint='3.11') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.1' #1469 8e13da5 mysqldb Interpreter(_hint='3.11') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient' ``` </details> <details> <summary>After</summary> ``` ❯ riot list "mysql$" No. Hash Name Interpreter Environment Packages #554 1a1c1fa mysql Interpreter(_hint='2.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #555 8ba1245 mysql Interpreter(_hint='2.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.23' #556 1e72d9c mysql Interpreter(_hint='3.5') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #557 16025dc mysql Interpreter(_hint='3.5') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.23' #558 1cdb1c5 mysql Interpreter(_hint='3.6') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #559 1230ff8 mysql Interpreter(_hint='3.6') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.29' #560 729b5a8 mysql Interpreter(_hint='3.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #561 1133f63 mysql Interpreter(_hint='3.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' #562 129b0f2 mysql Interpreter(_hint='3.8') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #563 12db871 mysql Interpreter(_hint='3.8') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' #564 270f95a mysql Interpreter(_hint='3.9') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #565 9182ae0 mysql Interpreter(_hint='3.9') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' #566 137147e mysql Interpreter(_hint='3.10') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python~=8.0.28' #567 f26af3b mysql Interpreter(_hint='3.10') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' #568 2eb00ea mysql Interpreter(_hint='3.11') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python~=8.0.31' #569 10d623a mysql Interpreter(_hint='3.11') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' ``` </details> ### Improve wait job runtime The `wait` job was not running with `-s` and was trying to install the dev package this meant it was taking 3+ minutes to run the `wait` job. We updated the job to ensure it does not install the dev package. Now it runs in <30 seconds. ### Properly restore workspace from build_base_venvs Through debugging we noticed that we were building the base venv/installing dev package in scenarios when we should not have. This is because we were storing the workspace/venvs from `build_base_venvs` incorrectly causing us to reinstall the dev package once per-job * per-node * per-python version. The fix was to checkout the code first and then restore the files from the workspace. The only lead I have as to why/when this started happening is when we added `.riot/requirements/` if the git checkout was overwriting the exiting `.riot/` directory added from the workspace? ### Adjusted parallelism Now that runtime of jobs is improved we can safe some credits by reducing parallelism. ## Checklist - [x] Change(s) are motivated and described in the PR description. - [x] Testing strategy is described if automated tests are not included in the PR. - [x] Risk is outlined (performance impact, potential for breakage, maintainability, etc). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] [Library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) are followed. If no release note is required, add label `changelog/no-changelog`. - [x] Documentation is included (in-code, generated user docs, [public corp docs](https://github.com/DataDog/documentation/)). - [x] Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [ ] Title is accurate. - [ ] No unnecessary changes are introduced. - [ ] Description motivates each change. - [ ] Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes unless absolutely necessary. - [ ] Testing strategy adequately addresses listed risk(s). - [ ] Change is maintainable (easy to change, telemetry, documentation). - [ ] Release note makes sense to a user of the library. - [ ] Reviewer has explicitly acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment. - [ ] Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)
This fix introduces a few changes to improve overall CircleCI job runtime. **Before:** - Runtime: 28+ minutes - Credits: 33k **After:** - Runtime: ~22 minutes (-20%) - Credits: ~15k (-55%) ### Fix mysql test pattern The `mysql` CircleCI job was using the pattern "mysql", which would also match the `mysqldb` jobs. This change updates the test pattern for all mysql jobs to include an ending `$`. <details> <summary>Before</summary> ``` ❯ riot list mysql No. Hash Name Interpreter Environment Packages #554 1a1c1fa mysql Interpreter(_hint='2.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #555 8ba1245 mysql Interpreter(_hint='2.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.23' #556 1e72d9c mysql Interpreter(_hint='3.5') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #557 16025dc mysql Interpreter(_hint='3.5') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.23' #558 1cdb1c5 mysql Interpreter(_hint='3.6') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #559 1230ff8 mysql Interpreter(_hint='3.6') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.29' #560 729b5a8 mysql Interpreter(_hint='3.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #561 1133f63 mysql Interpreter(_hint='3.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' #562 129b0f2 mysql Interpreter(_hint='3.8') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #563 12db871 mysql Interpreter(_hint='3.8') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' #564 270f95a mysql Interpreter(_hint='3.9') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #565 9182ae0 mysql Interpreter(_hint='3.9') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' #566 137147e mysql Interpreter(_hint='3.10') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python~=8.0.28' #567 f26af3b mysql Interpreter(_hint='3.10') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' #568 2eb00ea mysql Interpreter(_hint='3.11') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python~=8.0.31' #569 10d623a mysql Interpreter(_hint='3.11') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' #1450 9be82d5 mysqldb Interpreter(_hint='2.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=1.4.6' #1451 17a3e91 mysqldb Interpreter(_hint='3.5') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=1.4.6' #1452 1c338bf mysqldb Interpreter(_hint='3.6') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.0' #1453 162a527 mysqldb Interpreter(_hint='3.6') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.1' #1454 19ef42f mysqldb Interpreter(_hint='3.6') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient' #1455 145f708 mysqldb Interpreter(_hint='3.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.0' #1456 73a2d0b mysqldb Interpreter(_hint='3.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.1' #1457 11b2766 mysqldb Interpreter(_hint='3.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient' #1458 1fcb3db mysqldb Interpreter(_hint='3.8') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.0' #1459 1c5e6ed mysqldb Interpreter(_hint='3.8') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.1' #1460 137f08a mysqldb Interpreter(_hint='3.8') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient' #1461 149f779 mysqldb Interpreter(_hint='3.9') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.0' #1462 14a543a mysqldb Interpreter(_hint='3.9') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.1' #1463 1eea232 mysqldb Interpreter(_hint='3.9') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient' #1464 14a543a mysqldb Interpreter(_hint='3.9') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.1' #1465 1eea232 mysqldb Interpreter(_hint='3.9') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient' #1466 10bc6fe mysqldb Interpreter(_hint='3.10') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.1' #1467 bf00ea5 mysqldb Interpreter(_hint='3.10') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient' #1468 186525e mysqldb Interpreter(_hint='3.11') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient~=2.1' #1469 8e13da5 mysqldb Interpreter(_hint='3.11') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysqlclient' ``` </details> <details> <summary>After</summary> ``` ❯ riot list "mysql$" No. Hash Name Interpreter Environment Packages #554 1a1c1fa mysql Interpreter(_hint='2.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #555 8ba1245 mysql Interpreter(_hint='2.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.23' #556 1e72d9c mysql Interpreter(_hint='3.5') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #557 16025dc mysql Interpreter(_hint='3.5') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.23' #558 1cdb1c5 mysql Interpreter(_hint='3.6') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #559 1230ff8 mysql Interpreter(_hint='3.6') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.29' #560 729b5a8 mysql Interpreter(_hint='3.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #561 1133f63 mysql Interpreter(_hint='3.7') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' #562 129b0f2 mysql Interpreter(_hint='3.8') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #563 12db871 mysql Interpreter(_hint='3.8') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' #564 270f95a mysql Interpreter(_hint='3.9') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python==8.0.5' #565 9182ae0 mysql Interpreter(_hint='3.9') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' #566 137147e mysql Interpreter(_hint='3.10') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python~=8.0.28' #567 f26af3b mysql Interpreter(_hint='3.10') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' #568 2eb00ea mysql Interpreter(_hint='3.11') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python~=8.0.31' #569 10d623a mysql Interpreter(_hint='3.11') DD_TESTING_RAISE=1 DD_REMOTE_CONFIGURATION_ENABLED=false 'mock' 'pytest' 'pytest-mock' 'coverage' 'pytest-cov' 'opentracing' 'hypothesis<6.45.1' 'mysql-connector-python' ``` </details> ### Improve wait job runtime The `wait` job was not running with `-s` and was trying to install the dev package this meant it was taking 3+ minutes to run the `wait` job. We updated the job to ensure it does not install the dev package. Now it runs in <30 seconds. ### Properly restore workspace from build_base_venvs Through debugging we noticed that we were building the base venv/installing dev package in scenarios when we should not have. This is because we were storing the workspace/venvs from `build_base_venvs` incorrectly causing us to reinstall the dev package once per-job * per-node * per-python version. The fix was to checkout the code first and then restore the files from the workspace. The only lead I have as to why/when this started happening is when we added `.riot/requirements/` if the git checkout was overwriting the exiting `.riot/` directory added from the workspace? ### Adjusted parallelism Now that runtime of jobs is improved we can safe some credits by reducing parallelism. ## Checklist - [x] Change(s) are motivated and described in the PR description. - [x] Testing strategy is described if automated tests are not included in the PR. - [x] Risk is outlined (performance impact, potential for breakage, maintainability, etc). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] [Library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) are followed. If no release note is required, add label `changelog/no-changelog`. - [x] Documentation is included (in-code, generated user docs, [public corp docs](https://github.com/DataDog/documentation/)). - [x] Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [ ] Title is accurate. - [ ] No unnecessary changes are introduced. - [ ] Description motivates each change. - [ ] Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes unless absolutely necessary. - [ ] Testing strategy adequately addresses listed risk(s). - [ ] Change is maintainable (easy to change, telemetry, documentation). - [ ] Release note makes sense to a user of the library. - [ ] Reviewer has explicitly acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment. - [ ] Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)
cf. #1442