diff --git a/history.md b/history.md index b6c00c805a..78c087dd5d 100644 --- a/history.md +++ b/history.md @@ -41,10 +41,11 @@ Semantic Versioning 2.0.0 is from version 0.1.6+ ## List of versions -## version 0.6.2 - _(Unreleased)_ - 2024-06-?? {#v0.6.2} +## version 0.6.2 - _(Unreleased)_ - 2024-08-?? {#v0.6.2} - [x] (Changed) _Back-end_ Upgrade to .NET 8 - SDK 8.0.302 (Runtime: 8.0.6) (PR #1601) - [x] (Changed) _Front-end_ Upgrade npm packages (PR #1603) +- [x] (Fixed) _Back-end_ Query execution was interrupted, Regex Timeout (Issue #1628, #1590) (PR #1676) ## version 0.6.1 - _(Unreleased)_ - 2024-05-16 {#v0.6.1} diff --git a/starsky/starsky.foundation.database/Query/QueryGetAllRecursive.cs b/starsky/starsky.foundation.database/Query/QueryGetAllRecursive.cs index 675344d314..f43e481ce0 100644 --- a/starsky/starsky.foundation.database/Query/QueryGetAllRecursive.cs +++ b/starsky/starsky.foundation.database/Query/QueryGetAllRecursive.cs @@ -67,13 +67,21 @@ async Task> LocalQuery(ApplicationDbContext context) catch ( MySqlException exception ) { // https://github.com/qdraw/starsky/issues/1243 - if ( exception.Message.Contains("Timeout") ) + // https://github.com/qdraw/starsky/issues/1628 + if ( exception.ErrorCode is not (MySqlErrorCode.QueryTimeout or + MySqlErrorCode.LockWaitTimeout or + MySqlErrorCode.QueryInterrupted) ) { - return await LocalQuery(new InjectServiceScope(_scopeFactory) - .Context()); + _logger.LogError($"[GetAllRecursiveAsync] MySqlException ErrorCode: {exception.ErrorCode}"); + throw; } - - throw; + + _logger.LogInformation($"[GetAllRecursiveAsync] Next Retry Timeout/interrupted " + + $"{exception.ErrorCode} in GetAllRecursiveAsync"); + + await Task.Delay(1000); + return await LocalQuery(new InjectServiceScope(_scopeFactory) + .Context()); } } } diff --git a/starsky/starsky.foundation.database/Thumbnails/ThumbnailQuery.cs b/starsky/starsky.foundation.database/Thumbnails/ThumbnailQuery.cs index 350ca29c0e..0c49cec994 100644 --- a/starsky/starsky.foundation.database/Thumbnails/ThumbnailQuery.cs +++ b/starsky/starsky.foundation.database/Thumbnails/ThumbnailQuery.cs @@ -116,15 +116,23 @@ private async Task SaveChangesDuplicate(DbContext dbContext) } catch ( Exception exception ) { + // Check if the inner exception is a MySqlException + var mySqlException = exception as MySqlException; // Skip if Duplicate entry // MySqlConnector.MySqlException (0x80004005): Duplicate entry for key 'PRIMARY' // https://github.com/qdraw/starsky/issues/1248 https://github.com/qdraw/starsky/issues/1489 - if ( exception is MySqlException { ErrorCode: MySqlErrorCode.DuplicateKey } ) + if (mySqlException is { ErrorCode: MySqlErrorCode.DuplicateKey } + or { ErrorCode: MySqlErrorCode.DuplicateKeyEntry } ) { + _logger.LogInformation("[SaveChangesDuplicate] OK Duplicate entry error occurred: " + + $"{mySqlException.Message}"); return; } - _logger.LogError($"[SaveChangesDuplicate] T:{exception.GetType()} M:{exception.Message} I: {exception.InnerException}"); + _logger.LogError($"[SaveChangesDuplicate] T:{exception.GetType()} " + + $"M:{exception.Message} " + + $"I: {exception.InnerException} " + + $"ErrorCode: {mySqlException?.ErrorCode}"); throw; } @@ -230,9 +238,15 @@ public async Task RenameAsync(string beforeFileHash, string newFileHash) } } - private static async Task RenameInternalAsync(ApplicationDbContext dbContext, - string beforeFileHash, string newFileHash) + private async Task RenameInternalAsync(ApplicationDbContext dbContext, + string? beforeFileHash, string? newFileHash) { + if ( beforeFileHash == null || newFileHash == null) { + _logger.LogError($"[ThumbnailQuery] Null " + + $"beforeFileHash={beforeFileHash}; or newFileHash={newFileHash}; is null"); + return false; + } + var beforeOrNewItems = await dbContext.Thumbnails.Where(p => p.FileHash == beforeFileHash || p.FileHash == newFileHash).ToListAsync(); diff --git a/starsky/starsky.foundation.storage/Services/StructureService.cs b/starsky/starsky.foundation.storage/Services/StructureService.cs index ee0bd5d658..0528f1729e 100644 --- a/starsky/starsky.foundation.storage/Services/StructureService.cs +++ b/starsky/starsky.foundation.storage/Services/StructureService.cs @@ -230,7 +230,7 @@ private static List> ParseStructure(string structure, var matchCollection = new Regex(DateRegexPattern + "|{filenamebase}|\\*|.ext|.", - RegexOptions.None, TimeSpan.FromMilliseconds(200)) + RegexOptions.None, TimeSpan.FromMilliseconds(1000)) .Matches(structureItem); var matchList = new List(); diff --git a/starsky/starskytest/starsky.foundation.database/QueryTest/QueryGetAllRecursiveTest.cs b/starsky/starskytest/starsky.foundation.database/QueryTest/QueryGetAllRecursiveTest.cs index fcdaa41a54..58d020ad0e 100644 --- a/starsky/starskytest/starsky.foundation.database/QueryTest/QueryGetAllRecursiveTest.cs +++ b/starsky/starskytest/starsky.foundation.database/QueryTest/QueryGetAllRecursiveTest.cs @@ -74,7 +74,7 @@ await _query.AddItemAsync( [SuppressMessage("Usage", "S6602:\"Find\" method should be used instead of the \"FirstOrDefault\" extension")] [SuppressMessage("Usage", "S3398:move class")] - private static MySqlException CreateMySqlException(string message) + private static MySqlException CreateMySqlException(MySqlErrorCode code, string message) { // MySqlErrorCode errorCode, string? sqlState, string message, Exception? innerException @@ -88,7 +88,7 @@ private static MySqlException CreateMySqlException(string message) var instance = ( MySqlException ) ctor?.Invoke(new object[] { - MySqlErrorCode.AccessDenied, + code, "test", message, new Exception() @@ -100,10 +100,10 @@ private static MySqlException CreateMySqlException(string message) private class MySqlSaveDbExceptionContext : ApplicationDbContext { - private readonly string _error; + private readonly MySqlErrorCode _error; public MySqlSaveDbExceptionContext(DbContextOptions options, - string error) : base(options) + MySqlErrorCode error) : base(options) { _error = error; } @@ -116,19 +116,21 @@ public override DbSet FileIndex get { IsCalledMySqlSaveDbExceptionContext = true; - throw CreateMySqlException(_error); + throw CreateMySqlException(_error, "test error"); } set => IndexItems = value; } } - [TestMethod] - public async Task Retry_When_HitTimeout() + [DataTestMethod] // [Theory] + [DataRow(MySqlErrorCode.QueryTimeout)] + [DataRow(MySqlErrorCode.QueryInterrupted)] + public async Task Retry_When_HitTimeout(MySqlErrorCode code) { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(databaseName: "MovieListDatabase") .Options; - var fakeQuery = new Query(new MySqlSaveDbExceptionContext(options,"Timeout"), + var fakeQuery = new Query(new MySqlSaveDbExceptionContext(options,code), null!,CreateNewScope(), new FakeIWebLogger()); await fakeQuery.GetAllRecursiveAsync("test"); @@ -143,7 +145,7 @@ public async Task GeneralException() var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(databaseName: "MovieListDatabase") .Options; - var fakeQuery = new Query(new MySqlSaveDbExceptionContext(options,"Something else"), + var fakeQuery = new Query(new MySqlSaveDbExceptionContext(options,MySqlErrorCode.None), null!,CreateNewScope(), new FakeIWebLogger()); await fakeQuery.GetAllRecursiveAsync("test"); diff --git a/starsky/starskytest/starsky.foundation.database/Thumbnails/ThumbnailQueryErrorTest.cs b/starsky/starskytest/starsky.foundation.database/Thumbnails/ThumbnailQueryErrorTest.cs index 9912ddf5bb..8e898bbebf 100644 --- a/starsky/starskytest/starsky.foundation.database/Thumbnails/ThumbnailQueryErrorTest.cs +++ b/starsky/starskytest/starsky.foundation.database/Thumbnails/ThumbnailQueryErrorTest.cs @@ -239,8 +239,10 @@ private static MySqlException CreateMySqlException(string message, MySqlErrorCod } } - [TestMethod] - public async Task AddThumbnailRangeAsync_ShouldCatchPrimaryKeyHit() + [DataTestMethod] // [Theory] + [DataRow(MySqlErrorCode.DuplicateKey)] + [DataRow(MySqlErrorCode.DuplicateKeyEntry)] + public async Task AddThumbnailRangeAsync_ShouldCatchPrimaryKeyHit(MySqlErrorCode code) { IsCalledMySqlSaveDbExceptionContext = false; var options = new DbContextOptionsBuilder() @@ -248,7 +250,7 @@ public async Task AddThumbnailRangeAsync_ShouldCatchPrimaryKeyHit() .Options; var fakeQuery = new ThumbnailQuery( - new MySqlSaveDbExceptionContext(options, "Duplicate entry '1' for key 'PRIMARY'", MySqlErrorCode.DuplicateKey), + new MySqlSaveDbExceptionContext(options, "Duplicate entry '1' for key 'PRIMARY'", code), null!, new FakeIWebLogger()); await fakeQuery.AddThumbnailRangeAsync(new List diff --git a/starsky/starskytest/starsky.foundation.database/Thumbnails/ThumbnailQueryTest.cs b/starsky/starskytest/starsky.foundation.database/Thumbnails/ThumbnailQueryTest.cs index eaa139a85a..1366bc4cb4 100644 --- a/starsky/starskytest/starsky.foundation.database/Thumbnails/ThumbnailQueryTest.cs +++ b/starsky/starskytest/starsky.foundation.database/Thumbnails/ThumbnailQueryTest.cs @@ -595,6 +595,28 @@ public async Task RenameAsync_NotFound() var getter = await query.RenameAsync("not-found", "__new__hash__"); Assert.IsFalse(getter); } + + [TestMethod] + public async Task RenameAsync_Null_BeforeFileHash() + { + // Act + var query = new ThumbnailQuery(_context, null!, new FakeIWebLogger()); + + // Assert + var getter = await query.RenameAsync(null!, "__new__hash__"); + Assert.IsFalse(getter); + } + + [TestMethod] + public async Task RenameAsync_Null_NewFileHash() + { + // Act + var query = new ThumbnailQuery(_context, null!, new FakeIWebLogger()); + + // Assert + var getter = await query.RenameAsync("_test", null!); + Assert.IsFalse(getter); + } [TestMethod] public async Task RenameAsync_ShouldOverwrite()