diff --git a/Backend.Tests/Mocks/MongoDbContextMock.cs b/Backend.Tests/Mocks/MongoDbContextMock.cs index cbc3b028a7..1b6f1ec56e 100644 --- a/Backend.Tests/Mocks/MongoDbContextMock.cs +++ b/Backend.Tests/Mocks/MongoDbContextMock.cs @@ -15,6 +15,8 @@ public Task BeginTransaction() private sealed class MongoTransactionMock : IMongoTransaction { + public IClientSessionHandle Session => null!; + public Task CommitTransactionAsync() { return Task.CompletedTask; diff --git a/Backend/Contexts/MongoDbContext.cs b/Backend/Contexts/MongoDbContext.cs index 429396e1b8..4342e234bd 100644 --- a/Backend/Contexts/MongoDbContext.cs +++ b/Backend/Contexts/MongoDbContext.cs @@ -26,6 +26,8 @@ private class MongoTransactionWrapper(IClientSessionHandle session) : IMongoTran { private readonly IClientSessionHandle _session = session; + public IClientSessionHandle Session => _session; + public Task CommitTransactionAsync() { return _session.CommitTransactionAsync(); diff --git a/Backend/Interfaces/IMongoDbContext.cs b/Backend/Interfaces/IMongoDbContext.cs index 01827db0db..f7e57b8e17 100644 --- a/Backend/Interfaces/IMongoDbContext.cs +++ b/Backend/Interfaces/IMongoDbContext.cs @@ -12,6 +12,7 @@ public interface IMongoDbContext public interface IMongoTransaction : IDisposable { + IClientSessionHandle Session { get; } Task CommitTransactionAsync(); Task AbortTransactionAsync(); } diff --git a/Backend/Repositories/WordRepository.cs b/Backend/Repositories/WordRepository.cs index 60d204c124..d4eabcd8b1 100644 --- a/Backend/Repositories/WordRepository.cs +++ b/Backend/Repositories/WordRepository.cs @@ -15,6 +15,7 @@ namespace BackendFramework.Repositories [ExcludeFromCodeCoverage] public class WordRepository(IMongoDbContext dbContext) : IWordRepository { + private readonly IMongoDbContext _dbContext = dbContext; private readonly IMongoCollection _frontier = dbContext.Db.GetCollection("FrontierCollection"); private readonly IMongoCollection _words = dbContext.Db.GetCollection("WordsCollection"); @@ -100,9 +101,19 @@ public async Task DeleteAllWords(string projectId) var filterDef = new FilterDefinitionBuilder(); var filter = filterDef.Eq(x => x.ProjectId, projectId); - var deleted = await _words.DeleteManyAsync(filter); - await _frontier.DeleteManyAsync(filter); - return deleted.DeletedCount != 0; + using var transaction = await _dbContext.BeginTransaction(); + try + { + var deleted = await _words.DeleteManyAsync(transaction.Session, filter); + await _frontier.DeleteManyAsync(transaction.Session, filter); + await transaction.CommitTransactionAsync(); + return deleted.DeletedCount != 0; + } + catch + { + await transaction.AbortTransactionAsync(); + throw; + } } /// Removes all s from the Frontier for specified @@ -146,8 +157,18 @@ public async Task Create(Word word) OtelService.StartActivityWithTag(otelTagName, "creating a word in WordsCollection and Frontier"); PopulateBlankWordTimes(word); - await _words.InsertOneAsync(word); - await AddFrontier(word); + using var transaction = await _dbContext.BeginTransaction(); + try + { + await _words.InsertOneAsync(transaction.Session, word); + await _frontier.InsertOneAsync(transaction.Session, word); + await transaction.CommitTransactionAsync(); + } + catch + { + await transaction.AbortTransactionAsync(); + throw; + } return word; } @@ -170,8 +191,18 @@ public async Task> Create(List words) { PopulateBlankWordTimes(w); } - await _words.InsertManyAsync(words); - await AddFrontier(words); + using var transaction = await _dbContext.BeginTransaction(); + try + { + await _words.InsertManyAsync(transaction.Session, words); + await _frontier.InsertManyAsync(transaction.Session, words); + await transaction.CommitTransactionAsync(); + } + catch + { + await transaction.AbortTransactionAsync(); + throw; + } return words; } @@ -204,9 +235,20 @@ public async Task CreateAndDeleteFrontier(Word newWord, string oldWordId) otelTagName, "creating word in WordsCollection and Frontier, deleting old word from Frontier"); PopulateBlankWordTimes(newWord); - await _words.InsertOneAsync(newWord); - await _frontier.InsertOneAsync(newWord); - await _frontier.FindOneAndDeleteAsync(GetProjectWordFilter(newWord.ProjectId, oldWordId)); + using var transaction = await _dbContext.BeginTransaction(); + try + { + await _words.InsertOneAsync(transaction.Session, newWord); + await _frontier.InsertOneAsync(transaction.Session, newWord); + await _frontier.FindOneAndDeleteAsync( + transaction.Session, GetProjectWordFilter(newWord.ProjectId, oldWordId)); + await transaction.CommitTransactionAsync(); + } + catch + { + await transaction.AbortTransactionAsync(); + throw; + } return newWord; } @@ -224,8 +266,19 @@ public async Task AddAndDeleteFrontier(Word deletedWord, string wordId) otelTagName, "adding word to WordsCollection, deleting word from Frontier"); PopulateBlankWordTimes(deletedWord); - await _words.InsertOneAsync(deletedWord); - await _frontier.FindOneAndDeleteAsync(GetProjectWordFilter(deletedWord.ProjectId, wordId)); + using var transaction = await _dbContext.BeginTransaction(); + try + { + await _words.InsertOneAsync(transaction.Session, deletedWord); + await _frontier.FindOneAndDeleteAsync( + transaction.Session, GetProjectWordFilter(deletedWord.ProjectId, wordId)); + await transaction.CommitTransactionAsync(); + } + catch + { + await transaction.AbortTransactionAsync(); + throw; + } return deletedWord; }