Skip to content

[2.7][Translation] added message cache. #13986

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 1 commit into from

Conversation

aitboudad
Copy link
Contributor

Q A
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Fixed tickets #13948, #2570, #13676
Tests pass? yes
License MIT
  • tests
  • add MessageDoctrineCache
# app/config/config.yml
framework:
    translator:
        cache: translation.cache.doctrine # translation.cache.default

@@ -76,11 +70,15 @@ class Translator implements TranslatorInterface, TranslatorBagInterface
*
* @api
*/
public function __construct($locale, MessageSelector $selector = null, $cacheDir = null, $debug = false)
public function __construct($locale, MessageSelector $selector = null, $cache = null, $debug = false)
Copy link
Member

Choose a reason for hiding this comment

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

The docblock needs to be changed to take into account that $cache can be an instance of MessageCacheInterface.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@aitboudad aitboudad force-pushed the translation_catalogue_cache branch from e20e348 to 40f2628 Compare March 26, 2015 14:06
@aitboudad aitboudad changed the title [WIP][Translation] added message cache. [2.7][Translation] added message cache. Mar 26, 2015
@aitboudad
Copy link
Contributor Author

I think this could be a nice addition for 2.7, the default cache is useful in 80% of apps but when the size of catalogue became more than 10 MB it's consumes too much memory.
So the solution is to use DoctrineMessageCache, I tested with a catalogue(~100 mb) and the result look really interesting:

#config.yml
framework:
    translator:      { fallbacks: [en], cache: translation.cache.doctrine }

doctrine_cache:
    providers:
        translation_cache:
            memcached:
                servers:
                    localhost: 11211
#services.yml
translation.cache.doctrine:
    class: Symfony\Component\Translation\DoctrineMessageCache
    arguments: [%kernel.debug%, @translation_cache]

https://blackfire.io/profiles/compare/82c496fe-200a-451d-a2c8-e2fd458f0193/graph
selection_012

/cc @fabpot

@aitboudad aitboudad force-pushed the translation_catalogue_cache branch 4 times, most recently from b39dd88 to 45882cd Compare March 26, 2015 14:42
@aitboudad
Copy link
Contributor Author

@Tobion can you review this PR ?

@aitboudad aitboudad force-pushed the translation_catalogue_cache branch from 45882cd to b17354f Compare March 28, 2015 02:30
@@ -152,5 +153,13 @@
<service id="translation.extractor" class="%translation.extractor.class%"/>

<service id="translation.writer" class="%translation.writer.class%"/>

<!-- Cache -->
<service id="translation.cache.default" class="Symfony\Component\Translation\MessageCache" public="false">
Copy link
Member

Choose a reason for hiding this comment

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

You can make this service private. Then it could be inlined or removed by the compiler.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

isn't it ?

Copy link
Member

Choose a reason for hiding this comment

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

Yes it is already. Sorry.

@aitboudad
Copy link
Contributor Author

ping @symfony/deciders

@@ -652,6 +652,10 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder

$container->setParameter('translator.logging', $config['logging']);

if (isset($config['cache']) && $config['cache']) {
Copy link
Member

Choose a reason for hiding this comment

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

Isn't isset($config['cache']) alone sufficient enough?

@aitboudad aitboudad force-pushed the translation_catalogue_cache branch from b17354f to dfd828f Compare March 30, 2015 21:49
@aitboudad
Copy link
Contributor Author

any feedback, I really want it to be available in 2.7 :)

@xabbuh
Copy link
Member

xabbuh commented Mar 31, 2015

👍

@aitboudad aitboudad force-pushed the translation_catalogue_cache branch 2 times, most recently from d375b6d to 803e7d9 Compare April 1, 2015 09:06

<!-- Cache -->
<service id="translation.cache.default" class="Symfony\Component\Translation\MessageCache" public="false">
<argument key="cache_dir">%kernel.cache_dir%/translations</argument> <!-- cache_dir -->
Copy link
Member

Choose a reason for hiding this comment

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

The key can be removed as it's not used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@aitboudad aitboudad force-pushed the translation_catalogue_cache branch from 803e7d9 to eb33493 Compare April 2, 2015 07:00
* @param bool $debug Use cache in debug mode ?
* @param string $locale The locale
* @param MessageSelector|null $selector The message selector for pluralization
* @param string|null|MessageCacheInterface $cache The directory to use for the cache
Copy link
Member

Choose a reason for hiding this comment

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

the description is not accurate anymore

@fabpot
Copy link
Member

fabpot commented Apr 3, 2015

I tend to agree with @mpdude

@aitboudad
Copy link
Contributor Author

anyway the main thing here is to provide a doctrine cache for translation, what I didn't like for ConfigCache is it mainly related to resource file.

@aitboudad
Copy link
Contributor Author

any decision about this feature the current implementation look good to me :),
the #14178 look nice but didn't solve the issue at all,
another thing to consider here is that users can use the cache without the config component deps.

@mpdude
Copy link
Contributor

mpdude commented Apr 4, 2015

#14178 tries to solve a completely different problem.

But even with sticking to the current way of using ConfigCache, we should be able to improve the Translator by possibly having several caches (loading smaller MessageCatalogues) and/or improved memory efficiency by keeping messages in a shared memory cache (which this PR is about, if I understand it correctly).

@aitboudad aitboudad force-pushed the translation_catalogue_cache branch from cbd6c79 to f5c9a3d Compare April 4, 2015 12:44
@mpdude
Copy link
Contributor

mpdude commented Apr 6, 2015

If I got the code right (not sure about that), your goal is to keep messages in a key-value-store type cache.

That way, the Translator does not have to load a possibly huge MessageCatalogue (or even several of them) from the ConfigCache only to return a single message. It might be able to retrieve the message directly from the key-value-cache.

Also, the message catalogue will mostly be kept in shared memory and reduce the memory footprint for a single PHP process. With the current implementation, the entire Catalogue will be read from the ConfigCache into per-request memory.

So, if this understanding is correct, can't we find a way to make the Translator (key-value-)cache aware and use a lookup strategy based on (locale, domain, message-id) without having to touch the ConfigCache related parts at all?

Here's the steps such a Translator would need to do:

  1. Figure out if the catalogue is still fresh/valid

    For this, we could stick with the current ConfigCache validation. If we detect that we need to rebuild the ConfigCache, we subsequently also need to copy/map the Catalogue (after we loaded it) into the key-value-store/cache.

  2. Look into the cache

    Before requireing the Catalogue from the ConfigCache, check whether the requested (locale, domain, message-id) is available in the (key-value-)cache. If so, return it from there.

  3. Cache miss

    If a requested translation cannot be found, it does either not exist or it may be missing because the key-value-cache has been purged.

    We could detect the latter by having an extra key that is written once the cache was loaded. If that key is missing, re-populate the key-value-cache with the MessageCatalogue loaded from the ConfigCache (!).

Does that make sense?

@@ -659,6 +659,10 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder

$container->setParameter('translator.logging', $config['logging']);

if (isset($config['cache'])) {
Copy link
Member

Choose a reason for hiding this comment

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

You should prefix cache entries with something specific with the current installation to prevent conflicts if several Symfony apps run on the same server.

The example for the validator: https://github.com/aitboudad/symfony/blob/translation_catalogue_cache/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php#L774

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed.

@aitboudad
Copy link
Contributor Author

@mpdude you understand this topic now better than me :) , below my answers:

If I got the code right (not sure about that), your goal is to keep messages in a key-value-store type cache.

The main think here for me is to move the cache outside of Translator and adding an initial implementation of key-value-store(doctrine/cache).

can't we find a way to make the Translator (key-value-)cache aware

Right I think missed this part even if we use key-value-store we need ``ConfigCache` to make cache aware better.
I work now on this part so I'll fix it soon.

@aitboudad aitboudad force-pushed the translation_catalogue_cache branch from f5c9a3d to af78b62 Compare April 6, 2015 21:50
@aitboudad
Copy link
Contributor Author

@mpdude Hi,
If we decide to use the Translator (key-value-) we don't need anymore to save cache in file !! and I think the current limitations of ConfigCache is it doesn't support key/value cache.

So what I made now with 69225b8 is refresh cache when resources file change.

And to understand how I make the Translator (key-value-)cache aware:

1 - Figure out if the catalogue is still fresh/valid.

isFresh

2 - Look into the cache

check whether the requested (locale, domain, message-id) is available in the (key-value-)cache

3 - Cache miss

If that key is missing, re-populate the key-value-cache with the MessageCatalogue loaded from the Loaders and not ConfigCache !

@aitboudad aitboudad force-pushed the translation_catalogue_cache branch from 69225b8 to 2860af4 Compare April 7, 2015 10:19
@mpdude
Copy link
Contributor

mpdude commented Apr 7, 2015

If you're trying to go without ConfigCache, you will end up re-implementing the freshness check it provides (for example here). You also seem to need the DoctrineMessageCache as a replacement for ConfigCache.

My suggestion was to keep the ConfigCache part as-is. It's just a file sitting on the disk and does no harm. That way, you can still use the resource validation logic it provides. You will see from within Translator when the catalogue has to be rebuilt, that's when the cache-is-fresh check fails.

In other words, the DoctrineCache would just be another caching layer on top of ConfigCache: In Translator, before you load the entire Catalogue from disk, see if you can find that particular message in the DoctrineCache first.

When you have the Catalogue at hand anyways (because you had to rebuild it or load it because the message was not in DoctrineCache), also save it to the DoctrineCache.

@aitboudad
Copy link
Contributor Author

@mpdude well I agree to some extent, but I didn't like the idea of writing cache twice in ConfigCache then DoctrineCache.

@fabpot what do you think?

@jakzal
Copy link
Contributor

jakzal commented Apr 21, 2015

I'd hold off and do not merge this into 2.7. Note that I didn't review this PR in details just yet, and I only scanned through comments. Forgive me if I'm repeating someone else here.

We need to think of how to organise doctrine integration more carefully and make it consistent across the framework. With the current implementation I don't think that doctrine cache classes belong to the Doctrine bridge, as they're only use is the translator.

Serializer and Validator have their own cache implementations, slightly different from each other - first relies on doctrine/cache directly, the later adapts its interface. This PR introduces yet another approach. All I'm saying we should stop and consider if we can make it a bit more consistent.

@aitboudad
Copy link
Contributor Author

The safe way is to split this PR into 2, let's discuss about adding message cache interface and once accepted I'll open another one for doctrine cache.

@aitboudad aitboudad closed this May 3, 2015
@aitboudad aitboudad deleted the translation_catalogue_cache branch May 3, 2015 00:57
fabpot added a commit that referenced this pull request Jun 23, 2015
…ion for validator mapping (jakzal)

This PR was merged into the 2.8 branch.

Discussion
----------

[FrameworkBundle] Add a doctrine cache service definition for validator mapping

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

Following #12975, this PR only registers a new service so it's possible to use the new doctrine based cache implementation instead of the deprecated one. To use it, the end user would need to configure it in his `config.yml`:

```yaml
framework:
    validation:
        cache: validator.mapping.cache.doctrine.apc
```

In 3.0 we'll be able to replace the deprecated definition by aliasing `validator.mapping.cache.apc` to `validator.mapping.cache.doctrine.apc`.

I thought of automatic wrapping of services which implement doctrine interface, but decided it would be too magic.

I'm not convinced if APC is a good default anymore and hope for some discussion. I've used it as it's also used in serializer, and probably translation (see #13986). Since there's a built in opcache in more recent PHP versions, and apcu doesn't seem to be stable, there are better choices. Perhaps a better default would be a filesystem cache (not better performing, but it works anywhere).

Commits
-------

0642911 [FrameworkBundle] Add a doctrine cache service definition for validator mapping
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.

8 participants