P
P
Peter Gribanov2017-04-10 16:13:25
Doctrine ORM
Peter Gribanov, 2017-04-10 16:13:25

The query caching time affects the business logic. What to do?

The problem is insanely simple, but I do not see an adequate solution.
I have the entity News.
The news, among other things, has a publication date property (date_publication) that determines whether the news can be shown. That is the date after which the news can be published on the site.
This is the main business rule/restriction.
Accordingly, when displaying a list of news, I have to compare the Date of publication with the current time, and that's where I run into a caching problem.
We all know that requests using the current time are not cached. For example, MySQL does not cache queries that use the NOW()or function CURRENT_TIMESTAMP(). Therefore, I need to explicitly pass the current date:
date_publication <= "2017-04-10 13:50:00"
News is content that is usually not updated often, especially on resources where news is not the root context (Сore Сontext) .
I want to cache for 10 minutes a database query that returns a list of news. And in order for this to work, it's not enough for me to simply say - cache this request for 10 minutes. Here I run into a number of restrictions:

  • I am using Doctrine to access the DB ;
  • I'm using Doctrine's standard function setResultCacheLifetime()for caching query results , which automatically generates a cache ID based on the query parameters;
  • You can, of course, explicitly specify the cache ID setResultCacheId(), but this leads to overcomplication and, in fact, the implementation of almost the same functionality as the default;
  • Instead of Doctrine, any database engine that supports query caching can act here. I gave Doctrine as an example;
  • Under the hood, all requests are cached in Redis;
  • Business logic requires that I compare the actual current time, but in this case the cache ID will change every second and the cache will not work as a result.

I asked this question to the guys from Doctrine and they made me understand that this is not their concern and this task should be solved at a different level. And now I'm trying to understand at what level this task should be solved . Please help me with this.
Actually, the solution to this problem will be to round the current time to the cache time:
$now = new \DateTime();
// округляем дату вниз чтоб в выборку не попали данные которых там быть не должно
$now->setTimestamp(floor($now->getTimestamp() / $cacheLifetime) * $cacheLifetime);

But in this case
  1. The current tense turns out to be not real, and business requires that we use the present tense when describing restrictions;
  2. For rounding, we must use the request cache time and must specify it twice (or use twice) ;
  3. If we use the current date when describing the business rule, then it is not clear at what point we should round the date in the query.

To describe business rules, I use the Specification design pattern . And the specification describing the desired constraint can be a complex aggregate in which the Publish Date validation rule can be buried very deep. And pulling it out from there to round the date can be very difficult.
To understand how it might look, I will describe an example of using the specification:
$result = $rep->match(
  new AndX(
    new NewsPublished(),
    new Slice($slice_size, $slice_number)
  ),
  new Cache(600)
);

which expands into:
$result = $rep->match(
  new AndX(
    new Equals('enabled', true),
    new LessOrEqualThan('date_publication', new \DateTime()),
    new Limit($slice_size),
    new Offset(($slice_number - 1) * $slice_size)
  ),
  new Cache(600)
);

There is an idea that the rounding should be performed in a function match()that parses the specifications and collects an SQL query based on them. To apply the specifications, I use a third-party library and its author disagrees with me .
And in general, caching is about data access (Persistence Layer) and should not intersect with the domain level (Domain Layer) .
And for several days now I have been sitting and thinking. But what is more correct? It is clear that tucking a crutch is not a problem, but how is it right? So that the code is clear and transparent, easy to read, without crutches and consistent with a single language (Ubiquitous Language) .

Answer the question

In order to leave comments, you need to log in

2 answer(s)
D
dinegnet, 2017-04-10
@dinegnet

We all know that requests using the current time are not cached. For example, MySQL does not cache queries that use the NOW() or CURRENT_TIMESTAMP() function. Therefore, I need to explicitly pass the current date:
You're not caching there.
Relying on MySQL for caching is a stupid, inefficient strategy.
You are looking for a problem in the wrong place.

E
entermix, 2017-04-10
@entermix

I asked this question to the guys from Doctrine and they made me understand that this is not their concern and this task should be solved at a different level.

Everything is correct.
Just add a field published (bool), for example, and post pending news using the scheduler.
Accordingly, the sampleSELECT FROM `posts` WHERE `published` = 1

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question