-
-
Notifications
You must be signed in to change notification settings - Fork 377
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
Refactor storages and add StoreRegistry
#1330
Conversation
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.
Ok, gotta say I'm a bit annoyed. I feel like I'm returning to the same points again and again.
One thing is to remove the cache abstraction, fine. But I'm really not on board with removing the access to the default store from request, app etc. I think there are a lot of over complications here, and am overall degradation of DX.
Please wait until I'm done with the examples and reasoning. I specifically took care of addressing your particular concerns. |
Yup, i feel a bit stupid. Didn't see the description at all. This is actually very good and a big step forward. I apologize for being an idiot. All Looks good. Regarding the open questions - I'd say yes, let's expose a store property on the configs. |
@Goldziher thanks for the consideration. Please excuse the snarky tone in my comments to your remarks, I was a bit taken aback by your initial comment. But no hard feelings from my side ❤️ Let me get through with the examples and such and then let's see where this takes us.
Well, I too have an opinion on that, but I'll get to that in a separate comment 😬 |
1f425f7
to
225b72a
Compare
My personal thoughts on the two open questions:
Yes. If we want to facilitate the patterns explained in the above section, we should make this non-optional. There is little to gain from adding a third way to define stores for an integration and it will only make things more confusing an complex. The current solution allows to express every configuration a user wishes. If they absolutely need to bypass the registry, they can still customize the configuration objects.
I don't actually think I have anything to add there 🙃 |
StoreRegistry
StoreRegistry
367de39
to
3eec734
Compare
Signed-off-by: Janek Nouvertné <j.a.nouvertne@posteo.de>
Co-authored-by: Na'aman Hirschfeld <nhirschfeld@gmail.com>
48980ff
to
68d3d4e
Compare
Kudos, SonarCloud Quality Gate passed! |
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
Content
This builds upon #1304.
Starlite.cache_config
attributestarlite/storage
>starlite/stores
and update general naming to "store" instead of "storage"starlite/stores/registry.py
module, implementing aStoreRegistry
Starlite.stores
attribute, containing aStoreRegistry
stores
kwarg toStarlite
andAppConfig
to allow seeding of theStoreRegistry
RateLimitMiddleware
to useapp.storages
app.storages
app.storages
starlite/cache/config/
tostarlite/config/response_cache.py
CacheConfig
>ResponseCacheConfig
Starlite.cache_config
>response_cache_config
AppConfig.cache_config
>response_cache_config
starlite/cache
moduleASGIConnection.cache
propertyStarlite.cache
attributeThis fundamentally changes how different parts of the app interact with stores, and in its current state, requires less configuration by default.
At the core of this pattern is the
StoreRegistry
, which provides a central places where stores are registered under a name. The current implementation is not yet complete, and some details still have to be worked out. Open questions at the bottom.Examples
This allows configuration requiring very little boilerplate, but still offering access to the stores used:
By default, the default factory will return a new instance of
MemoryStore
, but we can configure it ourselves.In this example, we return the same
MemoryStore
instance every time.This pattern can also be used to conveniently build a store hierarchy. This simple configuration will use the same underlying redis connection, while making use of the namespacing feature. Every time a new store is requested via
StoreRegistry.get
, that hasn't been registered yet, the registry will callroot_store.with_namespace
, to create a new namespacedRedisStore
instance:the same can be done with a
FileStore
:It is also possible to explicitly set up stores:
or by passing a store name to the configs, which also allows to easily re-use stores:
Open questions
Should the store names used by various Starlite features be configurable or constant?(This has since been decided: Store names are configurable for each integration)
Currently, for the sake of keeping this RFC simple, the names of the stores used by Starlite internally (e.g. by
RateLimtiMiddleware
are hardcoded.A disadvantage of this is that it decouples the configuration value from where it's used. For example, there is a
RateLimitConfig
, but you can only specify which store it should use via another configuration value on the application.If we were to add a
store
field to those, it could be instead configure like:Should users be forced to go through the registry?
As of now, the only way to configure a store for e.g.
RateLimitMiddleware
is by setting it via thestores
kwarg toStarlite
(e.g.Starlite(..., stores={"rate_limit": my_store}
).The benefit of this is that all stores will always be registered, making them accessible everywhere, and configuration relatively simple, since there's only one way to set things up.
Should the registry include a "default cache" property?
Currently, the registry includes a
.default
property, which is equivalent toregistry.get("default")
. This is intended to mimic the.cache
property of theStarlite
app /ASGIConnection
, which this PR removes.The issue with using a default cache like this is that it returns the same instance, occupying the same namespace. This is not desirable in most cases, and can lead to issue if users do not pay attention to this. If we do not offer such a property, we would instead promote the pattern of using
registry.get(<my store>)
, to get a unique, namespaced store for each use case, while still allowing a user to override this behaviour (using thedefault_factory
) if they so wish.Especially for third party integrations that wish to make use of the stores provided by Starlite this pattern could prove very valuable, since no precautions have to be taken how to safely operate with a store without interfering with data not owned by this party.
If we don't offer a "default cache", we will also ensure that a user is always able to control which integration uses which store, via the registry.
Considerations
For convenience (
connection.app.stores.get("foo")
is quite complicated), we could proxyapp.stores
in these cases, likeconnection.cache
does in its current implementation.Should the registry be "frozen" after application startup?
Currently, you can register (and possibly override) stores at any time through the registry. This might lead to side effects if a store is acquired via
registry.get("some store")
, and later on overridden. We could add an option that prevents store overrides after application startup.