Suggested commit message:
Issue #2302617 by dawehner | Crell: Define a standard mechaism for backend-aware service overrides.
We've poured thousands of hours of work into piecing apart Drupal 8 so that anything that touches the database is swappable for an alternate backend. (MongoDB, Redis, Memcache, APC, Cassandra, you name it.) That's wonderful. Go Drupal!
However, we have no standard way of actually leveraging that capability. It's all ad-hoc container alters, which is problematic when you want to override only some core services with an alternate backend.
At the same time, as discussed inmaking "lowest common denominator" SQL for all databases is, at this point, a losing battle. Many places we're still using SQL could benefit greatly from backend-specific optimizations, and there's no reason that the exact same mechanism that can swap an SQL service for a MongoDB service can't also swap a MySQL service for a PostgreSQL service.
So let's define a standard way to override a "driver" service with a new driver backend that can handle MySQL/PostgreSQL/MongoDB/Cassandra/etc. all as equal citizens.
Note: This is JUST about the common "datastore-aware" services, which are the most likely to be swapped out. For everything else there's the existing ServiceModifierInterface that will continue to work exactly the same.
chx and I discussed this problem at length, and have come up with the following solution. I'll use the lock service as the example below for clarity.
1) Core defines a service, lock. It points to an SQL-generic implementation that aims for portability, NOT optimization.
2) That service is tagged "backend_overridable". (Tag name bikesheddable, but don't go overboard.)
3) The site-wide services.yml file for site-specific services has a container parameter defined in it, "default_backend". The installer will create this and set it to the DB driver name. (mysql, pgsql, sqlite, etc.)
4) A BackendCompilerPass object will do the following:
* The lock service is tagged backend_overridable. OK!
* Is lock an alias? If yes, stop.
* If not, look for a service named $default_backend.lock (taking $default_backend from the container parameter).
* Is there one? Change lock into an alias to $default_backend.lock.
(For all services with that tag.)
5) Now, the mongodb module can define a mongodb.lock service. It doesn't need to do anything else but define it.
6) If a site admin wants to replace just the lock service with the MongoDB version, he can do so by overriding the lock service in services.yml and aliasing it to mongodb.lock. kthxbye.
7) If a site admin wants to use MongoDB for everything possible, just change the default_backend parameter to "mongodb". BackendCompilerPass will then use the mongodb version for everything it can and leave the rest at the default SQL version.
8) Core can then enhance the MySQL, PostgreSQL, and SQLite drivers (assuming the latter two remain in core; if not contrib can do the same) to support driver-specific functionality. Then, we provide a mysql.lock service that uses those capabilities. Poof, BackendCompilerPass will now use the MySQL version of the lock service on a MySQL site (the 99% case). These alternates can be done piecemeal service by service without changing APIs.
Note that there's nothing core-specific about this approach. While very few contrib modules should have cause to write directly to a backend from a service, those that do can use the exact same mechanism above, opt-in, by just adding the tag to the service in question.
1) Agree on the above.
2) Implement the above.
User interface changes
Probably none. Only additional ways to use the container.
That also means this is not beta-blocking or beta-blocked. And we can add new override services to core at any time in the 8.x cycle without breaking anything.
PASSED: [[SimpleTest]]: [PHP 5.4 MySQL] 75,231 pass(es). View
FAILED: [[SimpleTest]]: [PHP 5.4 MySQL] Drupal installation failed. View
PASSED: [[SimpleTest]]: [PHP 5.4 MySQL] 75,323 pass(es). View