symfony-cmf / content-bundle

Provides some basic document classes and controllers for modeling content
https://cmf.symfony.com
19 stars 32 forks source link

HTTP-Cache headers #36

Open kingcrunch opened 11 years ago

kingcrunch commented 11 years ago

Of course I can use my own controller, but because symfony itself propogates the usage of HTTP-headers for cache-expiration/-validation I think it would be nice, if ContentBundle could provide something out of the box.

I for myself have no other idea than adding 5 ( :question: ) properties to the StaticContent-class

The controller should then take care to set the appropriate values of the Respose.

I don't know, if this is the best approach, so I think I'll leave it for discussion for now.

Also I am not that familar with the CR-concept as a whole, but something else, what comes into my mind, is a separate CacheProperties-node, that I can link with the content-node (or vice versa?).

hint: lets integrate with FOSHttpCacheBundle...

dbu commented 11 years ago

i think one interesting way to solve this could be to configure it per document class. if a document has mix:updated and whatever does the editing updates the timestamp on save, we already would have the information. maxage, shared_maxage and public need no data on the document.

i don't know if we need to allow editors to configure it on single documents. to me that seems like going too far. you could use a different default controller and have a assoc field on your document for those options.

last-modified and etag are also quite tricky, as the page usually consists of more than the main content. if any block or other dynamic part changed, last-modified would need to be updated too.

lsmith77 commented 11 years ago

maybe this should actually be part of the route data and not the content itself.

dbu commented 11 years ago

well the configuration what to use looks like a controller job to me, but could be on the route too. but the actual information (last modified and etag and such) needs to be in the content. actually when using blocks, this is not trivial at all to calculate, as you would need to walk all elements of the page.

uwej711 commented 11 years ago

The JCR spec for jcr:lastModified mentions automatic setting on no trivial subtree changes but it is not implemented in jackrabbit :-( (see https://issues.apache.org/jira/browse/JCR-2233)

ElectricMaxxx commented 10 years ago

While the controller could render its content alone, i would think this one could add the cache information to the data. Every part of the routing component (if this one would do it) had to ask the matched controller: can i have the cashe information? Maybe with an interface every (content) controller should serve.

dbu commented 10 years ago

we should also look into the caching support of SonataBlockBundle / SonataCacheBundle i guess. otherwise we will end with 2 caching logics, one for the CmfBlockBundle based on sonata and whatever we come up with here.

ElectricMaxxx commented 10 years ago

But i thought Sonata is used for backend only, or am i wrong informed? It is ok if sonata manages caching while creating backend stuff, but for the output in frontend the content building controller (or the part which renders the data/template) should do it on its own.

lsmith77 commented 10 years ago

We are using it in the frontend too .. blocks are handled by CmfBlockBundle which integrates with SonataBlockBundle

dbu commented 10 years ago

@ElectricMaxxx do not confuse SonataAdminBundle with the rest of sonata. sonata also provides a BlockBundle, a CacheBundle and so on. those bundles are not limited to admin backend use but provide general functionality.

ElectricMaxxx commented 10 years ago

Then i prefer a general solution. Not in the controller, but maybe in the routingBundle with an general Interface of the several controller to serve some data. at this point we could integrate the sonataCasheBundle in Case of the BlockBundle. In my opinion a separated solution (sonata for BlockBundle, nothing for some other or some own for this ContentBundle) is not the best way, cause a cms should contain a main Solution for caching, cause delivering content is the main task of a cms.

The question about what is actual and what not, is an other task, but could only be answered by the Controller, when he has the contentNode and decides which parts he wants to display.

dbu commented 10 years ago

we might also integrate / inspire with the LiipCacheControlBundle...

digitalkaoz commented 10 years ago

i stumbled on this a few minutes ago...i think integrating with LiipCacheControlBundle would be the most clean (SOC) way. but we dont have all information in the response object. maybe we could fire up a seperate event (with the document) from the controller and let the "LCCB" handle the rest, we would need some refactorings there too. the block problematic could be easily solved if they would all render as esi tags or am i wrong?

proper caching would be so nice! lets find a solution!

dbu commented 10 years ago

the SonataCacheBundle + SonataBlockBundle record each block that was used to render a page and can transfer that information to varnish. this is awesome as it allows to invalidate the varnish cache when one of the used blocks was changed. so i guess for blocks we really want to look in that direction. the same logic could also apply to pages that consist of aggregated data (i.e. a list of StaticPage documents).

@rande what exactly is the scope of the SonataCacheBundle? i know about the stuff i wrote above - does it also provide means to set cache lifetime (CacheControl: maxage...) and such things? or is that out of scope for the bundle?

@digitalkaoz lets see what thomas tells us and then either investigate the SonataCacheBundle or try to come up with an integration for LCCB. maybe the LCCB could have something to add custom services to add caching information during the response event, and we could do a listener (maybe directly in LCCB) that looks for a configurable object in the request and then offer a by-content-class mapping of caching information, like we now have the templates-by-class for the routing. if this scheme is extensible enough, we could provide optional support for a CachableInterface in ContentBundle with something that registers with LCCB to look at the actual documents ( i guess usually people should rather configure caching per class than on the instances stored in the database, except for special cases).

the algorithm should probably be looping over caching information providers and take the minimum/most restrictive value for each possible caching option. or pass on known caching information and have each provider override as it sees fit. then we would have the current configuration by path one provider, the configuration by cmfMainContent class another, and custom handlers that look at the cmfMainContent object (or whatever they need).

rande commented 10 years ago

The SonataCacheBundle does not alter the final response. It does not handle Response object, however you can store Respons object using these bundle.

The BlockBundle does provide some support for http cache with https://github.com/sonata-project/SonataBlockBundle/blob/master/Cache/HttpCacheHandler.php and https://github.com/sonata-project/SonataBlockBundle/blob/master/Block/BlockRenderer.php#L116-L160

The information provided by the BlockBundle can be used by other adapter to alter the final response. The HttpCacheHandler listener can be disable.

rande commented 10 years ago

Extra information about HttpCaching with block bundle : http://sonata-project.org/bundles/block/master/doc/reference/cache.html

dbu commented 10 years ago

i was hoping we could build something like @lsmith77 describes on his blog http://pooteeweet.org/blog/1979 - so afaict the sonata cache bundle / block bundle do not help us with that. this is not trivial - we could use a listener on phpcr-odm to record everything that is loaded, but that might well be recording too much. but without a listener, everything that outputs content needs to not forget to add that content to the list.

but block bundles does help with aggregating ttl for blocks that are used in a page. i wonder how we could extend that to also work for example on aggregate pages which lists any sort of content.

dbu commented 10 years ago

btw, phpcr itself seems to have support for etag with a mix:etag. see also https://github.com/jackalope/jackalope-doctrine-dbal/blob/master/src/Jackalope/Transport/DoctrineDBAL/Client.php#L1579

dbu commented 10 years ago

since https://github.com/jackalope/jackalope/pull/201 jackalope does support automated jcr:lastModified (even with jackrabbit, it just happens on the php side).

dbu commented 10 years ago

maybe the easiest for this would be a custom onKernelResponse listener. unless we come up with something really flexible and reusable, it makes more sense to just explain that a bit in the doc about caching, rather than providing something that most of the time has to be disabled and replaced anyways. @KingCrunch can you see the idea? do you have an idea what a generic onKernelResponse listener could do?

dbu commented 10 years ago

note that LiipCacheControlBundle is currently being refactored to FOSHttpCacheBundle: https://github.com/ddeboer/FOSHttpCacheBundle/

this could be a good oportunity to figure out extension points in the cache rules thing to make it work well with the cmf. any ideas how we could do that?

ElectricMaxxx commented 10 years ago

In general i would handle the setting of the metadata for caching stuff equal to the solution of the SeoBundle. We need to devide the properties in application setting and properties, which the user should be able to set. Do not know how the FOSHttpCacheBundle will handle it, but i think there is a way of putting those content own metadata to the header. And as @dbu gave the hint to use a hook on the request event, i would do it in here too. just waiting for a content of type CacheAware to let the FOSHttpCacheBundle do the work.

dbu commented 10 years ago

the FOSHttpCacheBundle relies on controllers and url patterns so far, not on content objects. but maybe we could do a custom matcher to match on for example content classes to generate cache headers.

caching information is something you usually do not want to manually maintain on every document. say i want new news to be cached for 30 minutes only, in case i need to update them. if they are older than a week, they should be cached for a day. if they are older than 6 months, lets cache them for a full week, as they never change. that would need custom php code to determine the category my content falls into... if i store that on the document, i would have to remember to edit old news to update caching information - that would never happen.

ElectricMaxxx commented 10 years ago

Ok i understand, that means there should be global settings only. But does the phpcr provide some values like updatedAt or createdAt? Cause this will be the values to handle with.

dbu commented 10 years ago

yes, documents can do that. i think a handler needs to know the document class, or maybe an interface that the cmf core bundle could provide. this functionality should not be linked to a particular storage engine - orm also has timestamps.