Skip to content

Introduce MetadataValidators to test arbitrary metadata for freshness. #15692

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

Closed
wants to merge 36 commits into from
Closed

Introduce MetadataValidators to test arbitrary metadata for freshness. #15692

wants to merge 36 commits into from

Conversation

bnw
Copy link

@bnw bnw commented Sep 4, 2015

Q A
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? yes
Tests pass? yes
Fixed tickets #7230
Related ticket #7176
License MIT
Doc PR TODO

This PR implements the idea discussed here: https://github.com/symfony/AsseticBundle/issues/168#issuecomment-13608074

Idea

Currently, any metadata that is passed to ConfigCache (namely implementations of ResourceInterface) is serialized to disk. Upon freshness check, it is unserialized and then asked, whether it is fresh.
The metadata has no chance to interact with any services, allowing only for very basic freshness checks.

With this PR, metadata no longer needs to implement the check for freshness itself. Instead, the ConfigCache::isFresh gets handed an array of MetadataValidator that take over the job of checking for freshness.
Each validator supports certain types of metadata. The implementations of ResourceInterface are now only a special type of metadata, that is validated using the ResourceInterfaceValidator.

In most cases, the ConfigCache should be used via the ConfigCacheFactory. I added a config_cache_factory service to which one can add validators via tagged services.

BC

Calling ConfigCache::isFresh without arguments is now deprecated.
Using ConfigCacheFactory without adding validators is deprecated.
However, both are still supported and yield the same results as before.

@mpdude
Copy link
Contributor

mpdude commented Sep 4, 2015

The PR header says BC break yes / deprecations no. Is that correct?

public function supports($metadata);

public function isFresh($metadata, $timestamp);

Copy link
Contributor

Choose a reason for hiding this comment

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

Missing docs for those 2 methods

*
* @author Matthias Pigulla <mp@webfactory.de>
*/
class ValidatorConfigCacheFactory implements ConfigCacheFactoryInterface
Copy link
Contributor

Choose a reason for hiding this comment

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

Why creating a whole new config cache factory given adding validators to the old one is doable without BC break ? (But will prevent the deprecation due to https://github.com/symfony/symfony/pull/15692/files#diff-9f40590069e1fa6582e56bcea49e09a4R77)
Will the ConfigCacheFactory trigger a deprecation saying to use the new ValidatorConfigCacheFactory instead ?

Copy link
Contributor

Choose a reason for hiding this comment

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

For BC reasons, the Router and Translator need to create a ConfigCacheFactory themselves if none is injected. If that factory comes without the default ResourceInterfaceValidator, they'd have to add one themselves and that would cause unnecessary dependencies. Having a default ResourceInterfaceValidator in ConfigCacheFactory would be weird as well.

So this way, use a ConfigCacheFactory if you don't care about special validations and you'll get the same behaviour as in the past.

Use a ValidatorConfigCacheFactory if you care about special validations or if you get the instance injected anyway and don't care about the hassle of setting it up.

This may have worked in the past, but was based on assumptions how the cache was implemented. You cannot dump a cache in prod and then expect it to be fresh or not when switching back to dev.

A useful test would:

1. Use a mock loader to detect reloads
2. In prod: Use a Translator to prime the cache; try again with another instance and see that the cache is used
3. In dev: Use a Translator to prime the cache; add an "always stale" resource; try again with another instance and see that the cache is refreshed.
* The first MetadataValidator that supports a given resource is considered authoritative.
* Resources with no matching MetadataValidators will silently be ignored and considered fresh.
*
* This method <em>does not</em> take the debug flag into consideration: Whether or not a cache
Copy link
Member

Choose a reason for hiding this comment

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

this looks weird to me. We need to take the debug flag into account

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd like to make that a decision of the client (the ConfigCacheFactory). In production, it may skip all freshness checks or use a special set of Validators only.

Then, the cache itself would not need to know about debug at all.

*
* @author Benjamin Klotz <bk@webfactory.de>
*/
interface MetadataValidatorInterface
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure MetadataValidator is the best name. This is more a resource checker than a validator

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm open for naming suggestions.

  • CacheChecker
  • CacheMetadataChecker
  • MetadataChecker
  • ResourceChecker
  • ResourceMetadataChecker

...?

Copy link
Member

Choose a reason for hiding this comment

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

I would say ResourceChecker

Copy link
Contributor

Choose a reason for hiding this comment

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

ResourceCheckerInterface, that is?

Copy link
Member

Choose a reason for hiding this comment

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

for the interface, yes

@stof
Copy link
Member

stof commented Sep 8, 2015

as discussed in #15719, I think we should keep a new ResourceInterface for resources instead of using object in the MetadataValidator. And this new interface should be accepted in places using resources in Symfony

@mpdude
Copy link
Contributor

mpdude commented Sep 8, 2015

Maybe I have a completely wrong picture of what the transition path for such changes must look like.

For example, the MessageCatalogueInterface is @api and contains an addResource(ResourceInterface ...) method. I don't see how we could reasonably switch to a new resource interface there? We must have a path (with E_USER_DEPRECATED messages if possible?) over 2.8, right?

This is why I am trying to clean up the non-@api ResourceInterface (see #15719).

But if you can show me the right way to introduce a new interface for resources, I'd be glad to give it a try.

@mpdude
Copy link
Contributor

mpdude commented Sep 8, 2015

What about this plan:

  • Add priorities so that new custom ResourceCheckers can be run first
  • Provide a default ResourceChecker with a very low priority that will validate through ResourceInterface::isFresh() but triggers a deprecation warning.
  • Add a new interface that extends ResourceInterface and contains the isFresh() method.
  • Add a ResourceChecker for this interface with a slightly higher priority and without deprecation warning.
  • Make FileResource et. al. implement the new interface.
  • Deprecate ResourceInterface::isFresh() but keep that method in the new interface subclass.
  • Also deprecate ResourceInterface::getResource() as per Deprecate ResourceInterface::getResource() #15719

We'll end up with ResourceInterface containing only __toString() in 3.0. Users implementing ResourceInterface will get a deprecation notice and can either switch to the new interface (no change beyond changing the "implements" list) or add a custom ResourceChecker to resolve it.

I acknowledge that @stof is in favor of having the new interface extend \Serializable as well, but I am not sure if that is possible in a BC way and still think that technically it is not necessary (i. e. should not stop us from getting all the rest).

* Add priorities so that new custom ResourceCheckers can be run first
* Provide a default ResourceChecker with a very low priority that will validate through ResourceInterface::isFresh() but triggers a deprecation warning.
* Add a new interface that extends ResourceInterface and contains the isFresh() method.
* Add a ResourceChecker for this interface with a slightly higher priority and without deprecation warning.
* Make FileResource et. al. implement the new interface.
* Deprecate ResourceInterface::isFresh() but keep that method in the new interface subclass.
@mpdude
Copy link
Contributor

mpdude commented Sep 9, 2015

I have implemented the above plan in #15738 and would like to close this PR in favor of it.

(I did not open this PR myself so I cannot close it.)

@mpdude
Copy link
Contributor

mpdude commented Sep 16, 2015

@bnw could you please close this?

@bnw bnw closed this Sep 16, 2015
@mpdude mpdude deleted the config-cache-metadata-validators branch September 24, 2015 19:20
fabpot added a commit that referenced this pull request Sep 25, 2015
…pdude)

This PR was squashed before being merged into the 2.8 branch (closes #15738).

Discussion
----------

Implement service-based Resource (cache) validation

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | #7230, #15692, #7782
| License       | MIT
| Doc PR        | symfony/symfony-docs#5136

### Overview

Currently, any metadata passed to `ConfigCache` (namely implementations of `ResourceInterface`) is serialized to disk. When the `ConfigCache` is validated, the metadata is unserialized and queried through `ResourceInterface::isFresh()` to determine whether the cache is fresh. That way, `ResourceInterface` implementations cannot interact with services, for example a database connection.

This PR introduces the new concept of `ResourceCheckers`. Services implementing `ResourceCheckerInterface` can be tagged as `config_cache.resource_checker` with an optional priority.

Clients that wish to use `ConfigCache` can then obtain an instance from the `config_cache_factory` service (which implements `ConfigCacheFactoryInterface`). The factory will take care of injecting resource checkers into the `ConfigCache` instance so that they can be used for cache validation.

Checking cache metadata is easy for `ResourceCheckers`:
* First, the `ResourceCheckerInterface::supports()` implementation is passed the metadata object in question. If the checker cannot handle the type of resource passed, `supports()` should return `false`.
* Otherwise, the `ResourceCheckerInterface::isFresh()` method will be called and given the resource as well as the timestamp at which the cache was initialized. If that method returns `false`, the cache is considered stale. If it returns `true`, the resource is considered unchanged and will *not* be passed to any additional checkers.

### BC and migration path

This PR does not (intend to) break BC but it comes with deprecations. The main reason is that `ResourceInterface` contains an `isFresh()` method that does not make sense in the general case of resources.

Thus, `ResourceInterface::isFresh()` is marked as deprecated and should be removed in Symfony 3.0. Resource implementations that can (or wish to) be validated in that simple manner can implement the `SelfCheckingResourceInterface` sub-interface that still contains (and will keep) the `isFresh()` method. The change should be as simple as changing the `extends` list.

Apart from that, `ResourceInterface` will be kept as the base interface for resource implementations. It is used in several `@api` interfaces and thus cannot easily be substituted.

For the Symfony 2.x series, a `BCResourceInterfaceChecker` will be kept that performs validation through `ResourceInterface::isFresh()` but will trigger a deprecation warning. The remedy is to either implement a custom ResourceChecker with a priority higher than -1000; or to switch to the aforementioned `SelfCheckingResourceInterface` which is used at a priority of -990 (without deprecation warning).

The `ConfigCache` and `ConfigCacheFactory` classes can be used as previously but do not feature checker-based cache validation.

### Outlook and closing remarks:

This PR supersedes #7230, #15692 and works at least in parts towards the goal of #7176.

The `ResourceCheckerInterface`, `...ConfigCache` and `...ConfigCacheFactory` no longer need to be aware of the `debug` flag. The different validation rules applied previously are now just a matter of `ResourceChecker` configuration (i. e. "no checkers" in `prod`).

It might be possible to remove the `debug` flag from Symfony's `Router` and/or `Translator` classes in the future as well because it was only passed on to the `ConfigCache` there.

Commits
-------

20d3722 Implement service-based Resource (cache) validation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants