Is it safe to use MongoDB ClientSessions & Transactions in C# async code?
Asked Answered
S

2

8

From the docs Read isolation consistency - Sessions (emphasis mine)

To provide causal consistency, MongoDB 3.6 enables causal consistency in client sessions. A causally consistent session denotes that the associated sequence of read and acknowledged write operations have a causal relationship that is reflected by their ordering. Applications must ensure that only one thread at a time executes these operations in a client session.

From the docs Transactions - Transactions & Sessions

Transactions are associated with a session. That is, you start a transaction for a session. At any given time, you can have at most one open transaction for a session.

  1. Does this say it is unsafe to use async/await, or at least that all the tasks using the session and transaction should - somehow - execute on on the same thread?

  2. Or does it say that the each async operation on the session needs to complete before another can be started?

  3. Or does it say that there can be multiple async operations running against the session, but all of those operations must be running on the same thread.

tl;dr
Is it safe to use async/await with transactions? If not, what is the best practice here?

e.g. Is this (admittedly horrible code) OK?

[HttpPost]
public async Task<IActionResult> PostAsync(CreateRequest createRequest)
{
    using (var session = await _client.StartSessionAsync())
    {
        await session.StartTransactionAsync();

        var inserts = new Task[] {
            _colHomer.InsertOneAsync(session, createRequest.Homer),
            _colMarge.InsertOneAsync(session, createRequest.Marge),
            _colBart.InsertOneAsync(session, createRequest.Bart)
        };

        await Task.WhenAll(inserts);

        await session.CommitTransactionAsync();
    }
}
Staub answered 14/8, 2018 at 10:51 Comment(4)
We are using async with transaction, but we have not found any issue so far, have you got anything ? even it is not raised as concern in there document also..Purism
It seems to be fine (async + transactions), we haven't gone into production or even load tested properly yet though. If there are issues I'll post here.Staub
Have you had any issues using mongo like that so far?Nonprofit
@MarekM. None so far, but again, not using production levels of data or concurrent transactions. Also, I've left that project and no longer have contact with it.Staub
B
2

Transaction and sessions are designed to be not thread safe. I think the above code will work, but it's not guarantied for all cases. UPDATE: however it's definitely save to use session/transaction in regular async way (without any manual task waiting and similar logic).

Brinkema answered 6/8, 2021 at 14:35 Comment(0)
S
1

We have recently been hit periodically by MongoCommandException with codeName: NoSuchTransaction and errorLabels: TransientTransactionError. After some investigation it proved to be caused by having multiple async read queries in the same transaction but without awaiting them. Here is "pseudo code" which shows the problem. This method was run multiple times on different threads:

using (var session = await _client.StartSessionAsync())
{
    await session.StartTransactionAsync();

    var docs1Task = _repositoryCollection1.GetAsync(id);
    var docs2Task = _repositoryCollection2.GetManyAsync(id);

    var docs1 = await docs1Task;
    var docs2 = await docs2Task; // <-- MongoCommandException

    await session.CommitTransactionAsync();
}

After awaiting the gets so they run synchronously the exception no longer occurred.

This question is the one which led me to suspect thread safety, although I didn't think it would be caused by reads. The actually message from the exception is:

"Command find failed: Given transaction number 38 does not match any in-progress transactions. The active transaction number is 37."

So it seems like there's some underlying mechanism in the transaction which increments the number.

This problem was happening with MongoDB.Driver version 2.21.0.

I'm leaving this answer here for information (and my future self...), and to conclude that transactions/sessions are indeed not thread safe, as dododo documented.

Subserve answered 28/9, 2023 at 8:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.