python-botocore/tests/functional/leak/test_resource_leaks.py
2022-05-25 15:10:07 -07:00

116 lines
5.4 KiB
Python

# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
from tests import BaseClientDriverTest
class TestDoesNotLeakMemory(BaseClientDriverTest):
# The user doesn't need to have credentials configured
# in order to run the functional tests for resource leaks.
# If we don't set this value and a user doesn't have creds
# configured, each create_client() call will have to go through
# the EC2 Instance Metadata provider's timeout, which can add
# a substantial amount of time to the total test run time.
INJECT_DUMMY_CREDS = True
# We're making up numbers here, but let's say arbitrarily
# that the memory can't increase by more than 10MB.
MAX_GROWTH_BYTES = 10 * 1024 * 1024
def test_create_single_client_memory_constant(self):
self.cmd('create_client', 's3')
self.cmd('free_clients')
self.record_memory()
for _ in range(100):
self.cmd('create_client', 's3')
self.cmd('free_clients')
self.record_memory()
start, end = self.memory_samples
self.assertTrue((end - start) < self.MAX_GROWTH_BYTES, (end - start))
def test_create_memory_clients_in_loop(self):
# We need to first create clients and free then before
# recording our memory samples. This is because of two reasons:
# 1. Caching. Some of the botocore internals will cache data, so
# the first client created will consume more memory than subsequent
# clients. We're interested in growing memory, not total
# memory usage (for now), so we we care about the memory in the
# steady state case.
# 2. Python memory allocation. Due to how python allocates memory
# via it's small object allocator, arena's aren't freed until the
# entire 256kb isn't in use. If a single allocation in a single
# pool in a single arena is still in use, the arena is not
# freed. This case is easy to hit, and pretty much any
# fragmentation guarantees this case is hit. The best we can
# do is verify that memory that's released back to python's
# allocator (but not to the OS) is at least reused in subsequent
# requests to create botocore clients.
self.cmd('create_multiple_clients', '200', 's3')
self.cmd('free_clients')
self.record_memory()
# 500 clients in batches of 50.
for _ in range(10):
self.cmd('create_multiple_clients', '50', 's3')
self.cmd('free_clients')
self.record_memory()
start, end = self.memory_samples
self.assertTrue((end - start) < self.MAX_GROWTH_BYTES, (end - start))
def test_create_single_waiter_memory_constant(self):
self.cmd('create_waiter', 's3', 'bucket_exists')
self.cmd('free_waiters')
self.record_memory()
for _ in range(100):
self.cmd('create_waiter', 's3', 'bucket_exists')
self.cmd('free_waiters')
self.record_memory()
start, end = self.memory_samples
self.assertTrue((end - start) < self.MAX_GROWTH_BYTES, (end - start))
def test_create_memory_waiters_in_loop(self):
# See ``test_create_memory_clients_in_loop`` to understand why
# waiters are first initialized and then freed. Same reason applies.
self.cmd('create_multiple_waiters', '200', 's3', 'bucket_exists')
self.cmd('free_waiters')
self.record_memory()
# 500 waiters in batches of 50.
for _ in range(10):
self.cmd('create_multiple_waiters', '50', 's3', 'bucket_exists')
self.cmd('free_waiters')
self.record_memory()
start, end = self.memory_samples
self.assertTrue((end - start) < self.MAX_GROWTH_BYTES, (end - start))
def test_create_single_paginator_memory_constant(self):
self.cmd('create_paginator', 's3', 'list_objects')
self.cmd('free_paginators')
self.record_memory()
for _ in range(100):
self.cmd('create_paginator', 's3', 'list_objects')
self.cmd('free_paginators')
self.record_memory()
start, end = self.memory_samples
self.assertTrue((end - start) < self.MAX_GROWTH_BYTES, (end - start))
def test_create_memory_paginators_in_loop(self):
# See ``test_create_memory_clients_in_loop`` to understand why
# paginators are first initialized and then freed. Same reason applies.
self.cmd('create_multiple_paginators', '200', 's3', 'list_objects')
self.cmd('free_paginators')
self.record_memory()
# 500 waiters in batches of 50.
for _ in range(10):
self.cmd('create_multiple_paginators', '50', 's3', 'list_objects')
self.cmd('free_paginators')
self.record_memory()
start, end = self.memory_samples
self.assertTrue((end - start) < self.MAX_GROWTH_BYTES, (end - start))