Skip to content
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

AWS account credentials in the Model Meta class is not used in threaded code #1160

Merged
merged 1 commit into from
Feb 21, 2023
Merged

AWS account credentials in the Model Meta class is not used in threaded code #1160

merged 1 commit into from
Feb 21, 2023

Conversation

atsuoishimoto
Copy link
Contributor

@atsuoishimoto atsuoishimoto commented Jan 31, 2023

Problem

An UnrecognizedClientException exception is thrown when using threads to reference a table.

How to reproduce

  1. Save the following code as repro.py and modify VALID_ACCESS_KEY and VALID_SECRET_ACCESS_KEY with valid AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.
import time, sys, os, traceback, io
from concurrent.futures import ThreadPoolExecutor
from pynamodb.models import Model
from pynamodb.attributes import UnicodeAttribute


# Set the credentials not to be read from ~/.aws/credentials
os.environ['AWS_ACCESS_KEY_ID'] = "INVALID_KEY"
os.environ['AWS_SECRET_ACCESS_KEY'] = "INVALID_SECRET"

class UserModel(Model):
    class Meta:
        table_name = 'test_accout_dynamodb222'
        region = 'us-west-1'
        aws_access_key_id = 'VALID_ACCESS_KEY' 
        aws_secret_access_key = 'VALID_SECRET_ACCESS_KEY'

    email = UnicodeAttribute(hash_key=True)
    first_name = UnicodeAttribute()
    last_name = UnicodeAttribute()

def init():
    # Initialize test table
    UserModel.create_table(wait=True,read_capacity_units=1, write_capacity_units=1)
    user = UserModel('test@example.com', first_name='Samuel', last_name='Adams')
    user.save()
    print("Initialized")
    return

if len(sys.argv) == 2 and int(sys.argv[1]):
    init()
    sys.exit(1)

def get_item(n):
    for i in range(10):
        for user in UserModel.query("test@example.com"):
            return print(n, i, user)

with ThreadPoolExecutor(max_workers=10) as e:
    futs = [e.submit(get_item, i) for i in range(10)]
    for fut in futs:
        try:
            fut.result()
        except Exception:
            traceback.print_exc()
            pass
  1. Create table with following command.
python3 repro.py 1
  1. Run the test with the following command.
python3 repro.py

Running repro.py will cause the following error.

Traceback (most recent call last):
  File "/Users/ishimoto/src/PynamoDB/pynamodb/connection/base.py", line 1387, in query
    return self.dispatch(QUERY, operation_kwargs, settings)
  File "/Users/ishimoto/src/PynamoDB/pynamodb/connection/base.py", line 340, in dispatch
    data = self._make_api_call(operation_name, operation_kwargs, settings)
  File "/Users/ishimoto/src/PynamoDB/pynamodb/connection/base.py", line 469, in _make_api_call
    raise VerboseClientError(
pynamodb.exceptions.VerboseClientError: An error occurred (UnrecognizedClientException) on request (QOC25V56NVI2T6UC2QM09B5TOVVV4KQNSO5AEMVJF66Q9ASUAAJG) on table (test_accout_dynamodb) when calling the Query operation: The security token included in the request is invalid.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/ishimoto/src/PynamoDB/reproduce.py", line 54, in <module>
    fut.result()
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/concurrent/futures/_base.py", line 451, in result
    return self.__get_result()
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/concurrent/futures/_base.py", line 403, in __get_result
    raise self._exception
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/Users/ishimoto/src/PynamoDB/reproduce.py", line 47, in get_item
    for user in UserModel.query("test@example.com"):
  File "/Users/ishimoto/src/PynamoDB/pynamodb/pagination.py", line 193, in __next__
    self._get_next_page()
  File "/Users/ishimoto/src/PynamoDB/pynamodb/pagination.py", line 179, in _get_next_page
    page = next(self.page_iter)
  File "/Users/ishimoto/src/PynamoDB/pynamodb/pagination.py", line 113, in __next__
    page = self._operation(*self._args, settings=self._settings, **self._kwargs)
  File "/Users/ishimoto/src/PynamoDB/pynamodb/connection/table.py", line 274, in query
    return self.connection.query(
  File "/Users/ishimoto/src/PynamoDB/pynamodb/connection/base.py", line 1389, in query
    raise QueryError("Failed to query items: {}".format(e), e)
pynamodb.exceptions.QueryError: Failed to query items: An error occurred (UnrecognizedClientException) on request (QOC25V56NVI2T6UC2QM09B5TOVVV4KQNSO5AEMVJF66Q9ASUAAJG) on table (test_accout_dynamodb) when calling the Query operation: The security token included in the request is invalid.

Cause

When botocore client lost credentials here, a new Session object is created with default credentials if the running thread does not own Session.

Resolution

Specify credentials when creating Session object.

@atsuoishimoto
Copy link
Contributor Author

Any progress on this issue?
Please let me know if you have any questions.

@ikonst ikonst added the bug label Feb 21, 2023
@ikonst ikonst merged commit 78a0faf into pynamodb:master Feb 21, 2023
@ikonst
Copy link
Contributor

ikonst commented Feb 21, 2023

Thanks for the fix, and sorry for the delay.

@atsuoishimoto
Copy link
Contributor Author

Thank you for the review! I deeply appreciate it.

ikonst pushed a commit that referenced this pull request Feb 21, 2023
When a model specifies custom AWS credentials (instead of global ones), they should be used when creating new sessions in threads. Previously, threads would always use the global credentials.
@ikonst ikonst added the backport 5.x Triggers backport to the 5.x branch label Feb 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport 5.x Triggers backport to the 5.x branch bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants