Merge pull request #128 from LD-Reborn/104-embedding-cache-store-exception-on-shutdown

104 embedding cache store exception on shutdown
This commit is contained in:
LD50
2026-02-21 22:23:31 +01:00
committed by GitHub
7 changed files with 32 additions and 24 deletions

View File

@@ -109,10 +109,19 @@ public class AIProvider
{ {
JObject responseContentJson = JObject.Parse(responseContent); JObject responseContentJson = JObject.Parse(responseContent);
List<JToken>? responseContentTokens = [.. responseContentJson.SelectTokens(embeddingsJsonPath)]; List<JToken>? responseContentTokens = [.. responseContentJson.SelectTokens(embeddingsJsonPath)];
if (responseContentTokens is null) if (responseContentTokens is null || responseContentTokens.Count == 0)
{ {
_logger.LogError("Unable to select tokens using JSONPath {embeddingsJsonPath} for string: {responseContent}.", [embeddingsJsonPath, responseContent]); if (responseContentJson.TryGetValue("error", out JToken? errorMessageJson) && errorMessageJson is not null)
throw new JSONPathSelectionException(embeddingsJsonPath, responseContent); {
string errorMessage = errorMessageJson.Value<string>() ?? "";
_logger.LogError("Unable to retrieve embeddings due to error: {errorMessage}", [errorMessage]);
throw new Exception($"Unable to retrieve embeddings due to error: {errorMessage}");
} else
{
_logger.LogError("Unable to select tokens using JSONPath {embeddingsJsonPath} for string: {responseContent}.", [embeddingsJsonPath, responseContent]);
throw new JSONPathSelectionException(embeddingsJsonPath, responseContent);
}
} }
return [.. responseContentTokens.Select(token => token.ToObject<float[]>() ?? throw new Exception("Unable to cast embeddings response to float[]"))]; return [.. responseContentTokens.Select(token => token.ToObject<float[]>() ?? throw new Exception("Unable to cast embeddings response to float[]"))];
} }

View File

@@ -109,7 +109,7 @@ public class SearchdomainController : ControllerBase
/// <param name="newName">Updated name of the searchdomain</param> /// <param name="newName">Updated name of the searchdomain</param>
/// <param name="settings">Updated settings of searchdomain</param> /// <param name="settings">Updated settings of searchdomain</param>
[HttpPut] [HttpPut]
public ActionResult<SearchdomainUpdateResults> Update([Required]string searchdomain, string newName, [FromBody]SearchdomainSettings? settings) public async Task<ActionResult<SearchdomainUpdateResults>> Update([Required]string searchdomain, string newName, [FromBody]SearchdomainSettings? settings)
{ {
(Searchdomain? searchdomain_, int? httpStatusCode, string? message) = SearchdomainHelper.TryGetSearchdomain(_domainManager, searchdomain, _logger); (Searchdomain? searchdomain_, int? httpStatusCode, string? message) = SearchdomainHelper.TryGetSearchdomain(_domainManager, searchdomain, _logger);
if (searchdomain_ is null || httpStatusCode is not null) return StatusCode(httpStatusCode ?? 500, new SearchdomainUpdateResults(){Success = false, Message = message}); if (searchdomain_ is null || httpStatusCode is not null) return StatusCode(httpStatusCode ?? 500, new SearchdomainUpdateResults(){Success = false, Message = message});
@@ -120,7 +120,7 @@ public class SearchdomainController : ControllerBase
{"name", newName}, {"name", newName},
{"id", searchdomain_.id} {"id", searchdomain_.id}
}; };
searchdomain_.helper.ExecuteSQLNonQuery("UPDATE searchdomain set name = @name WHERE id = @id", parameters); await searchdomain_.helper.ExecuteSQLNonQuery("UPDATE searchdomain set name = @name WHERE id = @id", parameters);
} else } else
{ {
Dictionary<string, dynamic> parameters = new() Dictionary<string, dynamic> parameters = new()
@@ -129,7 +129,7 @@ public class SearchdomainController : ControllerBase
{"settings", settings}, {"settings", settings},
{"id", searchdomain_.id} {"id", searchdomain_.id}
}; };
searchdomain_.helper.ExecuteSQLNonQuery("UPDATE searchdomain set name = @name, settings = @settings WHERE id = @id", parameters); await searchdomain_.helper.ExecuteSQLNonQuery("UPDATE searchdomain set name = @name, settings = @settings WHERE id = @id", parameters);
} }
return Ok(new SearchdomainUpdateResults(){Success = true}); return Ok(new SearchdomainUpdateResults(){Success = true});
} }
@@ -230,8 +230,9 @@ public class SearchdomainController : ControllerBase
/// Update the settings of a searchdomain /// Update the settings of a searchdomain
/// </summary> /// </summary>
/// <param name="searchdomain">Name of the searchdomain</param> /// <param name="searchdomain">Name of the searchdomain</param>
/// <param name="request">Settings to apply to the searchdomain</param>
[HttpPut("Settings")] [HttpPut("Settings")]
public ActionResult<SearchdomainUpdateResults> UpdateSettings([Required]string searchdomain, [Required][FromBody] SearchdomainSettings request) public async Task<ActionResult<SearchdomainUpdateResults>> UpdateSettings([Required]string searchdomain, [Required][FromBody] SearchdomainSettings request)
{ {
(Searchdomain? searchdomain_, int? httpStatusCode, string? message) = SearchdomainHelper.TryGetSearchdomain(_domainManager, searchdomain, _logger); (Searchdomain? searchdomain_, int? httpStatusCode, string? message) = SearchdomainHelper.TryGetSearchdomain(_domainManager, searchdomain, _logger);
if (searchdomain_ is null || httpStatusCode is not null) return StatusCode(httpStatusCode ?? 500, new SearchdomainUpdateResults(){Success = false, Message = message}); if (searchdomain_ is null || httpStatusCode is not null) return StatusCode(httpStatusCode ?? 500, new SearchdomainUpdateResults(){Success = false, Message = message});
@@ -240,7 +241,7 @@ public class SearchdomainController : ControllerBase
{"settings", JsonSerializer.Serialize(request)}, {"settings", JsonSerializer.Serialize(request)},
{"id", searchdomain_.id} {"id", searchdomain_.id}
}; };
searchdomain_.helper.ExecuteSQLNonQuery("UPDATE searchdomain set settings = @settings WHERE id = @id", parameters); await searchdomain_.helper.ExecuteSQLNonQuery("UPDATE searchdomain set settings = @settings WHERE id = @id", parameters);
searchdomain_.settings = request; searchdomain_.settings = request;
searchdomain_.queryCache.Capacity = request.QueryCacheSize; searchdomain_.queryCache.Capacity = request.QueryCacheSize;
return Ok(new SearchdomainUpdateResults(){Success = true}); return Ok(new SearchdomainUpdateResults(){Success = true});

View File

@@ -68,7 +68,7 @@ public class ServerController : ControllerBase
elementCount++; elementCount++;
embeddingsCount += entry.Keys.Count; embeddingsCount += entry.Keys.Count;
} }
var sqlHelper = DatabaseHelper.GetSQLHelper(_options.Value); var sqlHelper = _searchdomainManager.helper;
var databaseTotalSize = DatabaseHelper.GetTotalDatabaseSize(sqlHelper); var databaseTotalSize = DatabaseHelper.GetTotalDatabaseSize(sqlHelper);
Task<long> entityCountTask = DatabaseHelper.CountEntities(sqlHelper); Task<long> entityCountTask = DatabaseHelper.CountEntities(sqlHelper);
long queryCacheUtilization = 0; long queryCacheUtilization = 0;

View File

@@ -12,7 +12,7 @@ public class DatabaseHealthCheck : IHealthCheck
_searchdomainManager = searchdomainManager; _searchdomainManager = searchdomainManager;
_logger = logger; _logger = logger;
} }
public Task<HealthCheckResult> CheckHealthAsync( public async Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context, CancellationToken cancellationToken = default) HealthCheckContext context, CancellationToken cancellationToken = default)
{ {
try try
@@ -22,23 +22,23 @@ public class DatabaseHealthCheck : IHealthCheck
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogCritical("DatabaseHealthCheck - Exception occurred when retrieving and parsing database version: {ex}", ex.Message); _logger.LogCritical("DatabaseHealthCheck - Exception occurred when retrieving and parsing database version: {ex}", ex.Message);
return Task.FromResult( return await Task.FromResult(
HealthCheckResult.Unhealthy()); HealthCheckResult.Unhealthy());
} }
try try
{ {
_searchdomainManager.helper.ExecuteSQLNonQuery("INSERT INTO settings (name, value) VALUES ('test', 'x');", []); await _searchdomainManager.helper.ExecuteSQLNonQuery("INSERT INTO settings (name, value) VALUES ('test', 'x');", []);
_searchdomainManager.helper.ExecuteSQLNonQuery("DELETE FROM settings WHERE name = 'test';", []); await _searchdomainManager.helper.ExecuteSQLNonQuery("DELETE FROM settings WHERE name = 'test';", []);
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogCritical("DatabaseHealthCheck - Exception occurred when executing INSERT/DELETE query: {ex}", ex.Message); _logger.LogCritical("DatabaseHealthCheck - Exception occurred when executing INSERT/DELETE query: {ex}", ex.Message);
return Task.FromResult( return await Task.FromResult(
HealthCheckResult.Unhealthy()); HealthCheckResult.Unhealthy());
} }
return Task.FromResult( return await Task.FromResult(
HealthCheckResult.Healthy()); HealthCheckResult.Healthy());
} }
} }

View File

@@ -72,7 +72,7 @@ public static class CacheHelper
deletedEntries.Add(storeEntryIndex); deletedEntries.Add(storeEntryIndex);
} }
} }
Task removeEntriesFromStoreTask = RemoveEntriesFromStore(helper, deletedEntries); await RemoveEntriesFromStore(helper, deletedEntries);
List<(int Index, KeyValuePair<string, Dictionary<string, float[]>> Entry)> createdEntries = []; List<(int Index, KeyValuePair<string, Dictionary<string, float[]>> Entry)> createdEntries = [];
@@ -127,7 +127,6 @@ public static class CacheHelper
var taskSet = new List<Task> var taskSet = new List<Task>
{ {
removeEntriesFromStoreTask,
CreateEntriesInStore(helper, createdEntries), CreateEntriesInStore(helper, createdEntries),
UpdateEntryIndicesInStore(helper, changedEntries), UpdateEntryIndicesInStore(helper, changedEntries),
AddModelsToIndices(helper, AddedModels), AddModelsToIndices(helper, AddedModels),

View File

@@ -23,11 +23,10 @@ public class Searchdomain
public ConcurrentDictionary<string, Entity> entityCache; public ConcurrentDictionary<string, Entity> entityCache;
public ConcurrentBag<string> modelsInUse; public ConcurrentBag<string> modelsInUse;
public EnumerableLruCache<string, Dictionary<string, float[]>> embeddingCache; public EnumerableLruCache<string, Dictionary<string, float[]>> embeddingCache;
private readonly MySqlConnection connection;
public SQLHelper helper; public SQLHelper helper;
private readonly ILogger _logger; private readonly ILogger _logger;
public Searchdomain(string searchdomain, string connectionString, AIProvider aIProvider, EnumerableLruCache<string, Dictionary<string, float[]>> embeddingCache, ILogger logger, string provider = "sqlserver", bool runEmpty = false) public Searchdomain(string searchdomain, string connectionString, SQLHelper sqlHelper, AIProvider aIProvider, EnumerableLruCache<string, Dictionary<string, float[]>> embeddingCache, ILogger logger, string provider = "sqlserver", bool runEmpty = false)
{ {
_connectionString = connectionString; _connectionString = connectionString;
_provider = provider.ToLower(); _provider = provider.ToLower();
@@ -36,9 +35,7 @@ public class Searchdomain
this.embeddingCache = embeddingCache; this.embeddingCache = embeddingCache;
this._logger = logger; this._logger = logger;
entityCache = []; entityCache = [];
connection = new MySqlConnection(connectionString); helper = sqlHelper;
connection.Open();
helper = new SQLHelper(connection, connectionString);
settings = GetSettings(); settings = GetSettings();
queryCache = new(settings.QueryCacheSize); queryCache = new(settings.QueryCacheSize);
modelsInUse = []; // To make the compiler shut up - it is set in UpdateSearchDomain() don't worry // yeah, about that... modelsInUse = []; // To make the compiler shut up - it is set in UpdateSearchDomain() don't worry // yeah, about that...

View File

@@ -58,7 +58,7 @@ public class SearchdomainManager : IDisposable
} }
try try
{ {
return SetSearchdomain(searchdomain, new Searchdomain(searchdomain, connectionString, aIProvider, embeddingCache, _logger)); return SetSearchdomain(searchdomain, new Searchdomain(searchdomain, connectionString, helper, aIProvider, embeddingCache, _logger));
} }
catch (MySqlException) catch (MySqlException)
{ {
@@ -101,7 +101,9 @@ public class SearchdomainManager : IDisposable
{ "name", searchdomain }, { "name", searchdomain },
{ "settings", settings} { "settings", settings}
}; };
return await helper.ExecuteSQLCommandGetInsertedID("INSERT INTO searchdomain (name, settings) VALUES (@name, @settings)", parameters); int id = await helper.ExecuteSQLCommandGetInsertedID("INSERT INTO searchdomain (name, settings) VALUES (@name, @settings)", parameters);
searchdomains.Add(searchdomain, new(searchdomain, connectionString, helper, aIProvider, embeddingCache, _logger));
return id;
} }
public async Task<int> DeleteSearchdomain(string searchdomain) public async Task<int> DeleteSearchdomain(string searchdomain)