-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[DI] add ReverseContainer: a locator that turns services back to their ids #30334
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
[DI] add ReverseContainer: a locator that turns services back to their ids #30334
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.
I'm always amazed with your solutions 👍 !
780a6df
to
fe4d7de
Compare
f8a22fc
to
b6eefca
Compare
ping @symfony/deciders |
* | ||
* @throws ServiceNotFoundException When the service is not reversible | ||
*/ | ||
public function getService(string $id) |
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.
Why not let people use a normal service locator for this?
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.
Because it's not a normal service locator: you can get here only the ids you can fetch with the other method before. It wouldn't make sense to provide a standard locator interface here Having a different interface highlights this is not the same kind as a regular locator.
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.
isn't it the same as having a service locator for services tagged with container.reversible
that you can easily do manually?
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.
those, + public services
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.
So why does it need the method then?
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.
Because an instance of this class is a single consistent scope of services you can reverse.
Providing only object->id mapping here, and relying on another object to do the reverse would mean the two objects would be bound by some external convention that cannot be enforced by any contracts. Having both methods in this class is what provides the needed guarantee, from the abstraction pov.
b6eefca
to
a2515ee
Compare
9046dbb
to
6533251
Compare
6533251
to
3ffe1fe
Compare
3ffe1fe
to
c8dfc57
Compare
c8dfc57
to
ac1e429
Compare
…es back to their ids (nicolas-grekas) This PR was merged into the 4.3-dev branch. Discussion ---------- [DI] add ReverseContainer: a locator that turns services back to their ids | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - This PR introduces a `ReverseContainer`, which is a class you can type hint for to get it as a service. When you have a `ReverseContainer` at hand, you can then use it to know the service id of an object (if the object is not found, `null` is returned): `$id = $reverseContainer->getId($someObject);` You can also call `$reverseContainer->getService($id);` and get the service in return. To be reversible, a service must either be public or be tagged with `container.reversible`. I'm using this feature to serialize service references in a message, then send them through a Messenger bus, allowing the handler on the other side to use that referenced service to process the message. More specifically, my use case is sending messages for early cache expiration events through a bus and have a worker compute the soon-to-expire value in the background. The reversible services are the computation callbacks and the cache pools I need to compute the value for. Commits ------- ac1e429 [DI] add ReverseContainer: a locator that turns services back to their ids
…ing cached values in a worker (nicolas-grekas) This PR was merged into the 5.2-dev branch. Discussion ---------- [Cache] add integration with Messenger to allow computing cached values in a worker | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - ~~This PR needs and for now embeds #30334. See 2nd commit.~~ Using the new `CacheInterface` enables probabilistic early expiration by default. This means that from time to time, items are elected as early-expired while they are still fresh ([see Wikipedia](https://en.wikipedia.org/wiki/Cache_stampede#Probabilistic_early_expiration) for details). This PR adds a new `early_expiration_message_bus` option when configuring cache pools. When this option is set, cache pools are configured to send those early-expired items on a Messenger bus, then serve the current value immediately, while the updated value is computed in a worker in the background. `CacheInterface::get($key, $callback)` accepts any callable, but sending any callable on a bus is not possible (e.g. a `Closure` cannot be serialized). To bypass this constraint, this feature works only with callables in the form `[$service, 'publicMethod']`, where `$service` is any public or [reversible service](#30334). This constraint is a serious one: this $service must compute a value when knowing only its key. This means keys should embed enough information for this to happen. I think that's not that hard - and we may find ways to provide additional context in the future. At least the goal is worth it: in theory, this strategy allows achieving a 100% hit ratio even when invalidation-by-expiration is used. There are two things one needs to do to enable this behavior: 1. bind a message bus to a cache pool: ```yaml framework: cache: pools: test.cache: early_expiration_message_bus: messenger.default_bus ``` 2. route EarlyExpirationMessage to a transport: ```yaml framework: messenger: routing: 'Symfony\Component\Cache\Messenger\EarlyExpirationMessage': amqp ``` Commits ------- 6c0911f [Cache] add integration with Messenger to allow computing cached values in a worker
…ing cached values in a worker (nicolas-grekas) This PR was merged into the 5.2-dev branch. Discussion ---------- [Cache] add integration with Messenger to allow computing cached values in a worker | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - ~~This PR needs and for now embeds #30334. See 2nd commit.~~ Using the new `CacheInterface` enables probabilistic early expiration by default. This means that from time to time, items are elected as early-expired while they are still fresh ([see Wikipedia](https://en.wikipedia.org/wiki/Cache_stampede#Probabilistic_early_expiration) for details). This PR adds a new `early_expiration_message_bus` option when configuring cache pools. When this option is set, cache pools are configured to send those early-expired items on a Messenger bus, then serve the current value immediately, while the updated value is computed in a worker in the background. `CacheInterface::get($key, $callback)` accepts any callable, but sending any callable on a bus is not possible (e.g. a `Closure` cannot be serialized). To bypass this constraint, this feature works only with callables in the form `[$service, 'publicMethod']`, where `$service` is any public or [reversible service](symfony/symfony#30334). This constraint is a serious one: this $service must compute a value when knowing only its key. This means keys should embed enough information for this to happen. I think that's not that hard - and we may find ways to provide additional context in the future. At least the goal is worth it: in theory, this strategy allows achieving a 100% hit ratio even when invalidation-by-expiration is used. There are two things one needs to do to enable this behavior: 1. bind a message bus to a cache pool: ```yaml framework: cache: pools: test.cache: early_expiration_message_bus: messenger.default_bus ``` 2. route EarlyExpirationMessage to a transport: ```yaml framework: messenger: routing: 'Symfony\Component\Cache\Messenger\EarlyExpirationMessage': amqp ``` Commits ------- 6c0911f58c [Cache] add integration with Messenger to allow computing cached values in a worker
This PR introduces a
ReverseContainer
, which is a class you can type hint for to get it as a service.When you have a
ReverseContainer
at hand, you can then use it to know the service id of an object (if the object is not found,null
is returned):$id = $reverseContainer->getId($someObject);
You can also call
$reverseContainer->getService($id);
and get the service in return.To be reversible, a service must either be public or be tagged with
container.reversible
.I'm using this feature to serialize service references in a message, then send them through a Messenger bus, allowing the handler on the other side to use that referenced service to process the message. More specifically, my use case is sending messages for early cache expiration events through a bus and have a worker compute the soon-to-expire value in the background. The reversible services are the computation callbacks and the cache pools I need to compute the value for.