Skip to content

Commit

Permalink
Merge pull request #1676 from qdraw/feature/202408_bugfixes
Browse files Browse the repository at this point in the history
database queries
  • Loading branch information
qdraw authored Aug 29, 2024
2 parents e54d594 + 96ce12d commit 6f8d47c
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 23 deletions.
3 changes: 2 additions & 1 deletion history.md
Original file line number Diff line number Diff line change
Expand Up @@ -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}

Expand Down
18 changes: 13 additions & 5 deletions starsky/starsky.foundation.database/Query/QueryGetAllRecursive.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,21 @@ async Task<List<FileIndexItem>> 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());
}
}
}
Expand Down
22 changes: 18 additions & 4 deletions starsky/starsky.foundation.database/Thumbnails/ThumbnailQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -230,9 +238,15 @@ public async Task<bool> RenameAsync(string beforeFileHash, string newFileHash)
}
}

private static async Task<bool> RenameInternalAsync(ApplicationDbContext dbContext,
string beforeFileHash, string newFileHash)
private async Task<bool> 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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ private static List<List<StructureRange>> 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<StructureRange>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -88,7 +88,7 @@ private static MySqlException CreateMySqlException(string message)
var instance =
( MySqlException ) ctor?.Invoke(new object[]
{
MySqlErrorCode.AccessDenied,
code,
"test",
message,
new Exception()
Expand All @@ -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;
}
Expand All @@ -116,19 +116,21 @@ public override DbSet<FileIndexItem> 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<ApplicationDbContext>()
.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");
Expand All @@ -143,7 +145,7 @@ public async Task GeneralException()
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,16 +239,18 @@ 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<ApplicationDbContext>()
.UseInMemoryDatabase(databaseName: "MovieListDatabase")
.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<ThumbnailResultDataTransferModel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down

0 comments on commit 6f8d47c

Please sign in to comment.