Skip to main content
  1. About
  2. For Teams
Asked
Viewed 1k times
Part of PHP Collective
1

I have a Symfony application that shares the same codebase for many DBs.

It's quite typical, I have a single database for each "instance" (or "account") of my application, which is a simple medical appointments tool. I need to keep this architecture as is.

However, we are migrating from Symfony 2.8 to Symfony 4. Before Symfony 4, I had to create a "pseudo-prod" Symfony environment for each DB, because Symfony (with Doctrine) is very tied to having a single DB specified by the config, and even using env vars like SYMFONY__DATABASE__NAME, the database connection properties get hardcoded into the cache, so I needed to create a different environment for each app instance, hence creating a cache dir for each account, which is far from ideal.

Will the new env vars features from Symfony 4 make dynamic DB connection more feasible in a "native" manner?

Should I use another method to achieve this? (for example, creating a custom connection factory, etc)

I would like to have a single cache for my prod environment, and let the connection parameters be dynamic.

Any ideas appreciated. Thanks!

Note:

This is NOT a duplicate of: Using Relationships with Multiple Entity Managers

I am already using multiple entity managers, it has nothing to do with that.

6
  • symfony.com/doc/current/doctrine/multiple_entity_managers.html
    caramba
    –  caramba
    2018-02-04 20:34:00 +00:00
    Commented Feb 4, 2018 at 20:34
  • Thanks, I read that, but I hardly see such a pattern ("database_host2") as "dynamic", I don't have a single "customer", what if I have > 50 databases? Do I have to add an entity manager for each one, database_host3, database_host4? All these values get hardcoded into the cache, which prevents me from having a single cache environment for all DBs. Will the env shorthand introduced in 3.2 solve this ("password: "%env(DB_PASSWORD)%")?
    Pablo Hessualdo
    –  Pablo Hessualdo
    2018-02-04 21:09:59 +00:00
    Commented Feb 4, 2018 at 21:09
  • You would need to add a connection for each database that your application uses, in order for doctrine to connect to it. If you're using separate environments for multiple applications, then you can separate them out that way. Entities cannot define associations across different entity managers. If you need that, there are several alternatives that require some custom setup.
    Will B.
    –  Will B.
    2018-02-05 06:49:35 +00:00
    Commented Feb 5, 2018 at 6:49
  • Possible duplicate of Using Relationships with Multiple Entity Managers
    Will B.
    –  Will B.
    2018-02-05 06:50:40 +00:00
    Commented Feb 5, 2018 at 6:50
  • Yes, I get it, I'm actually using two entity managers, one for my client's data, which depends on its subdomain (say: johndoe.myapp.com), and one for some shared data, so I have two EMs, one for "myapp_shared" DB, and one for a "johndoe_data" DB. The thing is, DB connection data is hardcoded into the cache, which feels like nonsense IMO, because it forces me to have a cache dir per DB.
    Pablo Hessualdo
    –  Pablo Hessualdo
    2018-02-06 09:13:56 +00:00
    Commented Feb 6, 2018 at 9:13

1 Answer 1

2

Setting up your application for different hosts with Symfony 4 should be straight forward. You can just provide the DATABASE_URL as environment variable to each host, e.g. with nginx:

server {
    server_name domain.tld www.domain.tld;
    root /var/www/project/public;
    location / {
        try_files $uri /index.php$is_args$args;
    }
    location ~ ^/index\.php(/|$) {
        fastcgi_pass unix:/var/run/php7.1-fpm.sock;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;

        fastcgi_param DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name";

        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;

        internal;
    }
    location ~ \.php$ {
        return 404;
    }
    error_log /var/log/nginx/project_error.log;
    access_log /var/log/nginx/project_access.log;
}

The import bit is fastcgi_param DATABASE_URL ....

By default all instances will use the same cache dir, which is probably not what you want. Imagine customer A seeing customer B's data, because it's pulled from cache and B accessed it first.

A way around this is to modify src/Kernel.php to factor in some other env variable, the project's base name or some other info identifying each host and append that to the cache base directory defined in getCacheDir. By default it looks like this:

public function getCacheDir(): string
{
    return dirname(__DIR__).'/var/cache/'.$this->environment;
}

You could also use Symfony's built in cache component to specify different app.caches per host instead. This way you can possibly reuse the system cache responsible for caching the container, annotations, validations, etc.

You can find the cache configuration in your config/packages/framework.yml under framework.cache. See: https://speakerdeck.com/dbrumann/caching-in-symfony-an-overview?slide=37

Sign up to request clarification or add additional context in comments.

This is exactly what I'm doing, but using Apache. It looks like the DB connection properties (even when coming from environment variables) gets compiled into the class cache. Do you know any way of circumventing this?
What we are doing in my current project is extending the kernel to set an additional parameter in construct: $this->portalIdentifier = getenv('APP_PORTAL'); parent::__construct($environment, $debug); and then use that identifier to load different config files and append it to the cache directory. I don't think we encountered your issue, so without further info I don't think I can help.
Yes. We also use a Redis server where we could easily share caches should we feel the need to. Luckily storage and memory are not a big issue on our servers, so the added overhead from duplicate caches doesn't bother us.
Yes, I know it's not big, my current cache size is about 8Mb for every directory, so no big deal. I guess I could live with that, I just wanted to know if there was any way of avoiding repetition, as the only thing changing from dir to dir is the connection params... :(
If you use my approach with the additional portal identifier in the Kernel you could also change what name the Kernel returns and then use the same cache dir. Just make getName return the additional portal identifier or check how the filename for the compiled container is created. Then only the compiled container will be separate cache items but the remaining cache is the same. In theory this is the only place where the connection should be stored, so that should work and you can reuse the cache.

Your Answer

Draft saved
Draft discarded

Post as a guest

Required, but never shown

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.

lang-php
Morty Proxy This is a proxified and sanitized view of the page, visit original site.