Skip to content

Rework RegionBackend #6444

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

Merged
merged 8 commits into from
Jul 22, 2022
Merged

Rework RegionBackend #6444

merged 8 commits into from
Jul 22, 2022

Conversation

viren-nadkarni
Copy link
Member

@viren-nadkarni viren-nadkarni commented Jul 13, 2022

Summary

This PR introduces Stores, which supercede RegionBackends.

Stores provide in-memory storage for all provider data. They are analogous to the LocalStack's RegionBackend or Moto's BackendDict.

The terminology used, Store, is to avoid confusion with Backend which is used by Moto. The usage pattern is similar to Moto's where a singleton tracks all the stores for a service (see example below with sqs_stores)

This rework is influenced by Moto v4's approach of using nested dicts to delimit data by AWS account IDs and regions. The usage pattern is inspired by SQLAlchemy and its use of descriptor protocols.

Problem with RegionBackend

RegionBackend relies on voodoo magic to support multi-accounts. It is complex and breaks serialisation, causing compatibiltiy issues with persistence and cloud pods.

Usage pattern

# localstack.services.sqs.models
class SqsStore(BaseStore):
    queues = LocalAttribute(default=dict)  # Dict[str, str]
    DELETED = CrossRegionAttribute(default=dict)  # type: Dict[str, float]

sqs_stores = AccountRegionBundle('sqs', SqsStore)

# Usage
sqs_stores['111122223333']  # -> RegionStore
sqs_stores['11112222aaaa']  # -> ValueError! invalid Account ID
sqs_stores['111122223333']['ap-south-1']  # -> SqsStore
sqs_stores['111122223333']['zz-north-1']  # -> ValueError! invalid region

Followup in separate PRs

  • RegionBackends for all providers will be migrated to Stores
  • Compatibility with persistence and cloudpods
  • Remove RegionBackend code
  • Remove feature flag and make multi-accounts default (depends on Moto v4 #6430)

@viren-nadkarni viren-nadkarni self-assigned this Jul 13, 2022
@viren-nadkarni viren-nadkarni temporarily deployed to localstack-ext-tests July 13, 2022 10:31 Inactive
@coveralls
Copy link

coveralls commented Jul 13, 2022

Coverage Status

Coverage increased (+0.04%) to 91.686% when pulling bfa1f39 on provider-stores into d509cdf on master.

@github-actions
Copy link

github-actions bot commented Jul 13, 2022

LocalStack integration with Pro

       3 files         3 suites   1h 12m 43s ⏱️
1 128 tests 1 088 ✔️ 40 💤 0
1 445 runs  1 376 ✔️ 69 💤 0

Results for commit bfa1f39.

♻️ This comment has been updated with latest results.

@viren-nadkarni viren-nadkarni temporarily deployed to localstack-ext-tests July 13, 2022 12:11 Inactive
@viren-nadkarni viren-nadkarni temporarily deployed to localstack-ext-tests July 13, 2022 12:19 Inactive
@viren-nadkarni viren-nadkarni temporarily deployed to localstack-ext-tests July 13, 2022 13:38 Inactive
@viren-nadkarni viren-nadkarni temporarily deployed to localstack-ext-tests July 14, 2022 10:02 Inactive
@viren-nadkarni viren-nadkarni temporarily deployed to localstack-ext-tests July 14, 2022 11:47 Inactive
@thrau
Copy link
Member

thrau commented Jul 14, 2022

I love the direction this is taking @viren-nadkarni , really cool stuff! I particularly like the idea of scoping attributes explicitly via descriptors. the naming is also really clear and easy to understand. i played around a bit with my own version of this and noticed how you totally nailed all the abstractions!

here are some of my other initial thoughts:

  • instead of AccountRegionStore("sqs", SqsStore, validate=False) it would be awesome to just be able to call SqsStore(). i'm conflicted about this though since i do understand though that SqsStore is just a holder for the descriptors and not the actual thing storing the data. i think it would be more intuitive for developers though.
  • i'm not sure the nested datastructure is necessary. it seems to me we could just store container objects of the Dict[(Optional[str], Optional[str]), T]. depending on the type of attribute, we would create keys like (region, account), or (region, None), ...
  • accessing s3_store[account1][eu_region] requires me to have both the region and the account. but if i want to access a CrossRegion attribute, then i wouldn't really need to specify the account or the region, i could just access the s3_store.buckets["bucket-names-are-global"]. with the subscript, there's no way to easily do that.

i would love to introduce a type of View to abstract away the AccountRegionBackend class and not needing it to be subscriptable. i played around with a bit of code to implement that, but it's not as clean as yours :-)

API:

class S3Store(Store):
    buckets: Dict[str, Bucket] = GlobalAttribute()
    something_local: Dict[str, str] = LocalAttribute()

store = S3Store()

store.buckets["my-bucket"] # totally works because buckets is global

store.something_local["foo"]  # doesn't work (region/account context missing)

view = store.view(region="us-east-1", account="000...")
view.something_local["foo"] # works!
view.buckets["my-bucket"] # also works!

i have some code that makes this work, but the more i look at this though i'm unsure whether it's really better :-)

@viren-nadkarni viren-nadkarni temporarily deployed to localstack-ext-tests July 18, 2022 10:21 Inactive
@viren-nadkarni viren-nadkarni temporarily deployed to localstack-ext-tests July 18, 2022 10:26 Inactive
@viren-nadkarni
Copy link
Member Author

Thanks for the feedback @thrau!

Wrt to nested dicts, we could certainly use aggregate keys, and it would improve the performance too. However, I have a feeling that there might be benefits in using the nested structure just as Moto v4. Cloud pods and persistence mechanism logic would see dramatic simplification.

For the other points, they look promising candidates for following iterations!

@viren-nadkarni viren-nadkarni marked this pull request as ready for review July 18, 2022 11:55
@viren-nadkarni viren-nadkarni requested review from giograno and thrau July 18, 2022 11:56
Copy link
Member

@thrau thrau left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, as I already commented :-)

My only concern would be the similar naming of BaseStore and AccountRegionStore, which somehow indicates that one is derived by the other, where really BaseStore is the schema of the store, whereas AccountRegionStore is the actual instance managing the data. I'm wondering whether we could improve the naming to better reflect this. Either the Store is a schema/descriptor, or the AccountRegionStore is a view.

There's a slight mismatch there also in terms of functionality. Since the RegionStore/AccountRegionStore are managing the data of the store, it feels to me a bit unintuitive that the BaseStore is also doing state management (via reset), which is in the current structure something i would expect the AccountRegionStore to do.

I'm happy to merge this and then iterate afterwards when we start using it.

Also start calling encapsulations as Bundles
@viren-nadkarni viren-nadkarni temporarily deployed to localstack-ext-tests July 19, 2022 10:21 Inactive
@viren-nadkarni
Copy link
Member Author

viren-nadkarni commented Jul 19, 2022

@thrau I see what you actually meant by your original comment! Let's keep the using store for the model defintions and the encapsulating views as bundles -- thoughts?

Your other point about reset logic also makes sense 👍 It's been moved to RegionBundle

@viren-nadkarni viren-nadkarni merged commit e7efc76 into master Jul 22, 2022
@localstack localstack locked and limited conversation to collaborators Jul 22, 2022
@viren-nadkarni viren-nadkarni deleted the provider-stores branch July 22, 2022 08:20
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants