Inefficient polling of AWS resource Low

Custom polling can be inefficient and prone to error. Consider using AWS waiters instead. A waiter is an abstraction used to poll AWS resources, such as DynamoDB tables or Amazon S3 buckets, until a desired state is reached.

Detector ID
python/aws-polling-instead-of-waiter@v1.0
Category
Common Weakness Enumeration (CWE) external icon

Noncompliant example

1def polling_vs_waiters_noncompliant(response):
2    import boto3
3    ec2_client = boto3.client('ec2', region_name='us-east-1')
4    ec2_instance_id = response['Instances'][0]['InstanceId']
5
6    attempts = 0
7    while True:
8        print("Waiting for EC2 instance to be up")
9        # Noncompliant: uses custom polling instead of waiters feature.
10        rsp = ec2_client.describe_instance_status(
11            InstanceIds=[
12                str(ec2_instance_id)
13            ],
14            IncludeAllInstances=True
15        )
16
17        instance_status = rsp['Statuses'][0]['InstanceStatus']['Status']
18        system_status = rsp['Statuses'][0]['SystemStatus']['Status']
19
20        if str(instance_status) == 'ok' and str(system_status) == 'ok':
21            break
22        if str(instance_status) == 'impaired' or \
23                str(instance_status) == 'insufficient-data' or \
24                str(system_status) == 'failed' or \
25                str(system_status) == 'insufficient-data':
26            print('Instance status is ' + str(instance_status))
27            print('System status is ' + str(system_status))
28            tear_down()
29            exit(1)
30
31        attempts = attempts + 1
32        if attempts >= MAX_ATTEMPTS:
33            print("MAX wait time for EC2 instance to be up reached.")
34            print("Tearing down")
35            tear_down()
36            exit(1)
37
38        time.sleep(10)

Compliant example

1def polling_vs_waiters_compliant():
2    import boto3
3    client = boto3.client('kinesis', region_name='us-east-1')
4    # Setup the Kinesis with 1 shard.
5    stream_name = "tf_kinesis_test_1"
6    client.create_stream(StreamName=stream_name, ShardCount=1)
7    # Wait until stream exists, default is 10 * 18 seconds.
8    # Compliant: uses waiters feature.
9    client.get_waiter('stream_exists').wait(StreamName=stream_name)
10    for i in range(10):
11        data = "D" + str(i)
12        client.put_record(
13            StreamName=stream_name,
14            Data=data,
15            PartitionKey="TensorFlow" + str(i)
16            )