Reputation: 2979
I want to ask you if the following code will be executed in one atomic operation. I'm using mongodb c# driver.
The input to the method is list of id of the objects I want to update.
public void Update(IEnumerable<string> ids)
{
var query = Query<T>.Where(t => ids.Contains(t.Id));
var update = Update.Set("Modified", DateTime.Now); //this is just example of update
var options = new MongoUpdateOptions {Flags = UpdateFlags.Multi};
Collection.Update(query, update, options);
}
I'm interesting about the case, when I have milions of documents to update. What will happen if there will be a fail (power or hardware problem) during this update? Will be the database in a consistent state?
thanks.
Upvotes: 3
Views: 9181
Reputation: 2021
MongoDB >= 4.0 C# Driver >= 2.7 Here how i did it...
Goto "Sample Code"
In my DbContext class, where i had access to my client (IMongoClient), i defined sessions:
public IClientSessionHandle StartSession()
{
return _client.StartSession(new ClientSessionOptions());
}
public async Task<IClientSessionHandle> StartSessionAsync()
{
return await _client.StartSessionAsync(new ClientSessionOptions());
}
And as documentation says, they are capable of executing multiple transactions one after another, if only these transactions are coming one after another
A session is used to group together a series of operations that are related to each other and should be executed with the same session options. Sessions are also used for transactions.
these session said that they should be closed as soon as you ending your operation...
so you should actually write it like following, or perform manual disposing depend on your scenario:
// db is what i named my context, where i defined all my collections and database related stuffs.
// if you have direct access to client, you can call `StartSession/Async` exactly over the `client` object
using(var session = _db.StartSessionAsync()) {
//... All transactional code will go here
}
// Here, on ending the block, Will automatically call `Dispose` method, and the object will no longer exists
over session
object
Transactions are started, committed or aborted using methods of IClientSession. A session can only execute one transaction at a time, but a session can execute more than one transaction as long as each transaction is committed or aborted before the next one is started.
In this step you need to start a transaction before actually perform changes on database...
session.StartTransaction();
Once the session start, you should perform your transactions, and at the end...
If process is successful, you should call:
session.CommitTransaction();
Otherwise, you need to rollback
session.AbortTransaction();
As you can see i have two write operation on mongodb, and another external process which is critical in my case, and i need these three to be performed together,.. the first two are managed by transaction, and as long as the third doesn't throw exception, the database should keep its new state.
bool error;
using (var session = await _db.StartSessionAsync())
{
session.StartTransaction();
try
{
var deletedImage = _db.GetUserStore<ApplicationUser>().CollectionInstance.UpdateOneAsync(
Builders<ApplicationUser>.Filter.Where(w => w.Id == userId),
Builders<ApplicationUser>.Update.Pull(x => x.ProfilePictures, photoFromRepo));
await _db.ProfilePicture().DeleteAsync(new ObjectId(photoFromRepo.ImageId));
if (photoFromRepo.CloudinaryPublicId != null)
{
var deleteParams = new DeletionParams(photoFromRepo.CloudinaryPublicId);
var result = _cloudinary.Destroy(deleteParams);
if (result.Result == "ok")
{
// ignore
}
else
{
throw new Exception("Cannot delete file from cloud service...");
}
}
await session.CommitTransactionAsync();
error = false;
}
catch (Exception ex)
{
await session.AbortTransactionAsync();
error = true;
}
}
Does this work? Does it support multiple collections? Only god knows, i write this based on documentation and some sample i saw earlier today on my way home, and what i thought that may be right and possible ...
There are options that you can pass session, one of these option controls read/write concerns, and another one, control how much the data should be early before performing transactions (what it mean? i didn't got it myself, if you understand, please edit my post)
public class ClientSessionOptions
{
public bool? CausalConsistency { get; set; }
public TransactionOptions DefaultTransactionOptions { get; set; }
}
public class TransactionOptions
{
public ReadConcern ReadConcern { get; };
public ReadPreference ReadPreference { get; };
public WriteConcern WriteConcern { get; };
public TransactionOptions(
Optional<ReadConcern> readConcern = default(Optional<ReadConcern>),
Optional<ReadPreference> readPreference = default(Optional<ReadPreference>),
Optional<WriteConcern> writeConcern = default(Optional<WriteConcern>));
public TransactionOptions With(
Optional<ReadConcern> readConcern = default(Optional<ReadConcern>),
Optional<ReadPreference> readPreference = default(Optional<ReadPreference>),
Optional<WriteConcern> writeConcern = default(Optional<WriteConcern>))
}
Upvotes: 1
Reputation: 11515
MongoDB do not support transaction or atomic multi documents. MongoDB perform atomic operation only on one document.
You can check this in the documentation of Mongodb
So if you update with your query 1000 documents and your server crash during this operation, some documents may be updated, other won't.
Upvotes: 0