Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Consistently using the value of "now" throughout the transaction

I'm looking for guidelines to using a consistent value of the current date and time throughout a transaction.

By transaction I loosely mean an application service method, such methods usually execute a single SQL transaction, at least in my applications.

Ambient Context

One approach described in answers to this question is to put the current date in an ambient context, e.g. DateTimeProvider, and use that instead of DateTime.UtcNow everywhere.

However the purpose of this approach is only to make the design unit-testable, whereas I also want to prevent errors caused by unnecessary multiple querying into DateTime.UtcNow, an example of which is this:

// In an entity constructor:
this.CreatedAt = DateTime.UtcNow;
this.ModifiedAt = DateTime.UtcNow;

This code creates an entity with slightly differing created and modified dates, whereas one expects these properties to be equal right after the entity was created.

Also, an ambient context is difficult to implement correctly in a web application, so I've come up with an alternative approach:

Method Injection + DeterministicTimeProvider

  • The DeterministicTimeProvider class is registered as an "instance per lifetime scope" AKA "instance per HTTP request in a web app" dependency.
  • It is constructor-injected to an application service and passed into constructors and methods of entities.
  • The IDateTimeProvider.UtcNow method is used instead of the usual DateTime.UtcNow / DateTimeOffset.UtcNow everywhere to get the current date and time.

Here is the implementation:

/// <summary>
/// Provides the current date and time.
/// The provided value is fixed when it is requested for the first time.
/// </summary>
public class DeterministicTimeProvider: IDateTimeProvider
{
    private readonly Lazy<DateTimeOffset> _lazyUtcNow =
        new Lazy<DateTimeOffset>(() => DateTimeOffset.UtcNow);

    /// <summary>
    /// Gets the current date and time in the UTC time zone.
    /// </summary>
    public DateTimeOffset UtcNow => _lazyUtcNow.Value;
}

Is this a good approach? What are the disadvantages? Are there better alternatives?

like image 347
Gebb Avatar asked Sep 24 '15 00:09

Gebb


People also ask

When a transaction reads a value which is a value of an uncommitted transaction is known as?

Dirty Reads A dirty read occurs when a transaction reads data that has not yet been committed. For example, suppose transaction 1 updates a row.

What is read consistency?

"Read Consistency" is one of the "Transaction Isolation" levels that describe how good concurrent transactions are isolated from each-other (i.e. in how far they can treat the database as if only they work on it).

What is Repeatable Read isolation level?

Repeatable Read Isolation Level. The Repeatable Read isolation level only sees data committed before the transaction began; it never sees either uncommitted data or changes committed during transaction execution by concurrent transactions.


2 Answers

Sorry for the logical fallacy of appeal to authority here, but this is rather interesting:

John Carmack once said:

There are four principle inputs to a game: keystrokes, mouse moves, network packets, and time. (If you don't consider time an input value, think about it until you do -- it is an important concept)"

Source: John Carmack's .plan posts from 1998 (scribd)

(I have always found this quote highly amusing, because the suggestion that if something does not seem right to you, you should think of it really hard until it seems right, is something that only a major geek would say.)

So, here is an idea: consider time as an input. It is probably not included in the xml that makes up the web service request, (you wouldn't want it to anyway,) but in the handler where you convert the xml to an actual request object, obtain the current time and make it part of your request object.

So, as the request object is being passed around your system during the course of processing the transaction, the time to be considered as "the current time" can always be found within the request. So, it is not "the current time" anymore, it is the request time. (The fact that it will be one and the same, or very close to one and the same, is completely irrelevant.)

This way, testing also becomes even easier: you don't have to mock the time provider interface, the time is always in the input parameters.

Also, this way, other fun things become possible, for example servicing requests to be applied retroactively, at a moment in time which is completely unrelated to the actual current moment in time. Think of the possibilities. (Picture of bob squarepants-with-a-rainbow goes here.)

like image 70
Mike Nakis Avatar answered Jan 12 '23 09:01

Mike Nakis


Hmmm.. this feels like a better question for CodeReview.SE than for StackOverflow, but sure - I'll bite.

Is this a good approach?

If used correctly, in the scenario you described, this approach is reasonable. It achieves the two stated goals:

  1. Making your code more testable. This is a common pattern I call "Mock the Clock", and is found in many well-designed apps.

  2. Locking the time to a single value. This is less common, but your code does achieve that goal.

What are the disadvantages?

  1. Since you are creating another new object for each request, it will create a mild amount of additional memory usage and additional work for the garbage collector. This is somewhat of a moot point since this is usually how it goes for all objects with per-request lifetime, including the controllers.

  2. There is a tiny fraction of time being added before you take the reading from the clock, caused by the additional work being done in loading the object and from doing lazy loading. It's negligible though - probably on the order of a few milliseconds.

  3. Since the value is locked down, there's always the risk that you (or another developer who uses your code) might introduce a subtle bug by forgetting that the value won't change until the next request. You might consider a different naming convention. For example, instead of "now", call it "requestRecievedTime" or something like that.

  4. Similar to the previous item, there's also the risk that your provider might be loaded with the wrong lifecycle. You might use it in a new project and forget to set the instancing, loading it up as a singleton. Then the values are locked down for all requests. There's not much you can do to enforce this, so be sure to comment it well. The <summary> tag is a good place.

  5. You may find you need the current time in a scenario where constructor injection isn't possible - such as a static method. You'll either have to refactor to use instance methods, or will have to pass either the time or the time-provider as a parameter into the static method.

Are there better alternatives?

Yes, see Mike's answer.

You might also consider Noda Time, which has a similar concept built in, via the IClock interface, and the SystemClock and FakeClock implementations. However, both of those implementations are designed to be singletons. They help with testing, but they don't achieve your second goal of locking the time down to a single value per request. You could always write an implementation that does that though.

like image 28
Matt Johnson-Pint Avatar answered Jan 12 '23 09:01

Matt Johnson-Pint