feat: exceptions
Some checks failed
build-packages / meilisearch-dotnet-packages (push) Has been cancelled

This commit is contained in:
Damien 2025-03-01 14:09:25 -05:00
parent ab8c4398f8
commit 430e1d8617
11 changed files with 100 additions and 87 deletions

View File

@ -1,12 +0,0 @@
namespace meilisearch.NET.Exceptions;
public class DocumentBatchException : DocumentManagementException
{
public int BatchSize { get; }
public DocumentBatchException(int batchSize, Exception innerException)
: base($"Failed to process batch of {batchSize} documents", innerException)
{
BatchSize = batchSize;
}
}

View File

@ -1,10 +0,0 @@
namespace meilisearch.NET.Exceptions;
/// <summary>
/// Exception thrown when there are issues with document management
/// </summary>
public class DocumentManagementException : MeiliSearchException
{
public DocumentManagementException(string message) : base(message) { }
public DocumentManagementException(string message, Exception innerException) : base(message, innerException) { }
}

View File

@ -1,7 +0,0 @@
namespace meilisearch.NET.Exceptions;
public class DocumentSyncException : DocumentManagementException
{
public DocumentSyncException(string message, Exception innerException)
: base($"Failed to sync documents: {message}", innerException) { }
}

View File

@ -1,7 +0,0 @@
namespace meilisearch.NET.Exceptions;
public class DocumentValidationException : DocumentManagementException
{
public DocumentValidationException(string message)
: base($"Document validation failed: {message}") { }
}

View File

@ -0,0 +1,12 @@
namespace meilisearch.NET.Exceptions;
public class IndexAlreadyExistsException : IndexManagementException
{
public string IndexName { get; }
public IndexAlreadyExistsException(string indexName)
: base($"Index '{indexName}' already exists")
{
IndexName = indexName;
}
}

View File

@ -1,7 +1,7 @@
namespace meilisearch.NET.Exceptions;
public class IndexLimitExceededException : IndexManagementException
public class IndexLimitReachedException : IndexManagementException
{
public IndexLimitExceededException()
public IndexLimitReachedException()
: base("Maximum number of indexes (1000) has been reached") { }
}

View File

@ -2,5 +2,5 @@
public class ProcessStartException : ProcessManagementException
{
public ProcessStartException(string message) : base($"Failed to start Meilisearch process: {message}") { }
public ProcessStartException(Exception innerException, string message) : base($"Failed to start Meilisearch process: {message}", innerException) { }
}

View File

@ -2,5 +2,5 @@
public class ProcessStopException : ProcessManagementException
{
public ProcessStopException(string message) : base($"Failed to stop Meilisearch process: {message}") { }
public ProcessStopException(Exception innerException, string message) : base($"Failed to stop Meilisearch process: {message}", innerException) { }
}

View File

@ -1,7 +1,9 @@
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using Meilisearch;
using meilisearch.NET.Exceptions;
using meilisearch.NET.Interfaces;
using meilisearch.NET.Services.ProcessManagement;
using Microsoft.Extensions.Logging;
namespace meilisearch.NET.Services.DocumentManagement;
@ -9,13 +11,15 @@ namespace meilisearch.NET.Services.DocumentManagement;
public class DocumentManager:IDocumentManager
{
private readonly ILogger<DocumentManager> _logger;
private readonly MeiliSearchProcessManager _meiliSearchProcessManager;
private readonly MeilisearchClient _client;
private const int THRESHOLD = 100;
private ObservableCollection<KeyValuePair<string,IDocument>> _documentCollection;
public DocumentManager(MeilisearchClient client, ILogger<DocumentManager> logger)
public DocumentManager(MeilisearchClient client, ILogger<DocumentManager> logger, MeiliSearchProcessManager meiliSearchProcessManager)
{
_meiliSearchProcessManager = meiliSearchProcessManager;
_logger = logger;
_client = client;
_documentCollection = new ObservableCollection<KeyValuePair<string,IDocument>>();
@ -24,6 +28,9 @@ public class DocumentManager:IDocumentManager
public async Task AddDocumentAsync(string repositoryId, IDocument document, bool autoCommit = false)
{
if (!_meiliSearchProcessManager.IsProcessRunning())
throw new ProcessNotRunningException();
_logger.LogTrace($"Adding document '{document.Id}' to repository '{repositoryId}'...");
_documentCollection.Add(new KeyValuePair<string, IDocument>(repositoryId, document));
_logger.LogInformation($"Document {document.Id} added to collection.");
@ -34,6 +41,9 @@ public class DocumentManager:IDocumentManager
}
public void AddDocument(string repositoryId, IDocument document, bool autoCommit = false)
{
if (!_meiliSearchProcessManager.IsProcessRunning())
throw new ProcessNotRunningException();
_logger.LogTrace($"Adding document '{document.Id}' to repository '{repositoryId}'...");
_documentCollection.Add(new KeyValuePair<string, IDocument>(repositoryId, document));
_logger.LogInformation($"Document {document.Id} added to collection.");
@ -44,6 +54,9 @@ public class DocumentManager:IDocumentManager
}
public void SyncDocumentsToServer()
{
if (!_meiliSearchProcessManager.IsProcessRunning())
throw new ProcessNotRunningException();
var grouped = _documentCollection.GroupBy(pair => pair.Key)
.ToDictionary(group => group.Key, group => group.Select(pair => pair.Value).ToList());
foreach (var repository in grouped)
@ -53,9 +66,12 @@ public class DocumentManager:IDocumentManager
_documentCollection.Clear();
var result = RetryAsync(() => repositoryIndex.AddDocumentsAsync(repository.Value, "id")).Result;
}
}
}
public async Task SyncDocumentsToServerAsync()
{
if (!_meiliSearchProcessManager.IsProcessRunning())
throw new ProcessNotRunningException();
var grouped = _documentCollection.GroupBy(pair => pair.Key)
.ToDictionary(group => group.Key, group => group.Select(pair => pair.Value).ToList());
foreach (var repository in grouped)

View File

@ -1,6 +1,7 @@
using System.IO.Compression;
using System.Reflection;
using Meilisearch;
using meilisearch.NET.Exceptions;
using meilisearch.NET.Interfaces;
using meilisearch.NET.Services.ProcessManagement;
using Meilisearch.QueryParameters;
@ -16,35 +17,36 @@ public class IndexManager:IIndexManager
private readonly string _indexBasePath = Path.Combine(AppContext.BaseDirectory, "db", "indexes" );
private readonly ILogger<IndexManager> _logger;
private readonly MeilisearchClient _client;
private readonly MeiliSearchProcessManager _processManager;
private readonly MeiliSearchProcessManager _meiliSearchProcessManager;
public IndexManager(ILogger<IndexManager> logger, MeilisearchClient client, MeiliSearchProcessManager processManager)
public IndexManager(ILogger<IndexManager> logger, MeilisearchClient client, MeiliSearchProcessManager meiliSearchProcessManager)
{
_processManager = processManager;
_meiliSearchProcessManager = meiliSearchProcessManager;
_client = client;
_logger = logger;
}
public Task<List<string>> GetAllIndexes()
public async Task<List<string>> GetAllIndexes()
{
throw new NotImplementedException();
_logger.LogTrace("Fetching all indexes from Meilisearch server created with the SDK...");
var result = _client.GetAllIndexesAsync().Result.Results.Select(x => x.Uid).Where(x=>x!="index_bindings").ToList();
_logger.LogInformation($"Fetched {result.Count} indexes from Meilisearch server.");
return result;
}
public void CreateIndex<T>(string indexName) where T : IDocument
{
if (!_meiliSearchProcessManager.IsProcessRunning())
throw new ProcessNotRunningException();
var indexes = GetAllIndexes().Result;
if(indexes.Count>=1000)
{
_logger.LogWarning("Maximum number of indexes reached, cannot create new index.");
return;
}
throw new IndexLimitReachedException();
if (indexes.Any(x => x == indexName))
{
_logger.LogWarning($"Index {indexName} already exists, skipping creation of index.");
return;
}
throw new IndexAlreadyExistsException(indexName);
var foldersBefore = Directory.GetDirectories(_indexBasePath);
_logger.LogTrace($"Creating index '{indexName}'...");
@ -74,18 +76,16 @@ public class IndexManager:IIndexManager
}
public async Task CreateIndexAsync<T>(string indexName) where T : IDocument
{
if (!_meiliSearchProcessManager.IsProcessRunning())
throw new ProcessNotRunningException();
var indexes = await GetAllIndexes();
if(indexes.Count>=1000)
{
_logger.LogWarning("Maximum number of indexes reached, cannot create new index.");
return;
}
throw new IndexLimitReachedException();
if (indexes.Any(x => x == indexName))
{
_logger.LogWarning($"Index {indexName} already exists, skipping creation of index.");
return;
}
throw new IndexAlreadyExistsException(indexName);
var foldersBefore = Directory.GetDirectories(_indexBasePath);
_logger.LogTrace($"Creating index '{indexName}'...");
@ -114,11 +114,13 @@ public class IndexManager:IIndexManager
}
public void DeleteIndex(string indexName)
{
if (!_meiliSearchProcessManager.IsProcessRunning())
throw new ProcessNotRunningException();
var indexes = _client.GetAllIndexesAsync().Result;
if (indexes.Results.Any(x => x.Uid == indexName)==false)
{
_logger.LogWarning($"Index '{indexName}' does not exist, skipping deletion of index.");
return;
throw new IndexNotFoundException(indexName);
}
_logger.LogTrace($"Deleting index '{indexName}'...");
_client.DeleteIndexAsync(indexName).Wait();
@ -127,11 +129,13 @@ public class IndexManager:IIndexManager
}
public async Task DeleteIndexAsync(string indexName)
{
if (!_meiliSearchProcessManager.IsProcessRunning())
throw new ProcessNotRunningException();
var indexes = _client.GetAllIndexesAsync().Result;
if (indexes.Results.Any(x => x.Uid == indexName)==false)
{
_logger.LogWarning($"Index '{indexName}' does not exist, skipping deletion of index.");
return;
throw new IndexNotFoundException(indexName);
}
_logger.LogTrace($"Deleting index '{indexName}'...");
await _client.DeleteIndexAsync(indexName);
@ -140,6 +144,9 @@ public class IndexManager:IIndexManager
}
public void SetIndexEnabled(string indexName, bool enabled)
{
if (!_meiliSearchProcessManager.IsProcessRunning())
throw new ProcessNotRunningException();
_logger.LogTrace($"Updating index '{indexName}' status to {enabled}...");
if(enabled)
{
@ -153,6 +160,9 @@ public class IndexManager:IIndexManager
}
public async Task SetIndexEnabledAsync(string indexName, bool enabled)
{
if (!_meiliSearchProcessManager.IsProcessRunning())
throw new ProcessNotRunningException();
_logger.LogTrace($"Updating index '{indexName}' status to {enabled}...");
if(enabled)
{
@ -166,6 +176,9 @@ public class IndexManager:IIndexManager
}
public long GetIndexStorageUsage(string indexName, bool useCompressedSize = true)
{
if (!_meiliSearchProcessManager.IsProcessRunning())
throw new ProcessNotRunningException();
var doc = _client.GetIndexAsync("index_bindings").Result.GetDocumentAsync<Models.Index>(indexName).Result;
if (doc.IsCompressed)
@ -176,21 +189,22 @@ public class IndexManager:IIndexManager
var indexPath = GetIndexFilePath(indexName).Result+".zip";
if (!File.Exists(indexPath))
{
_logger.LogWarning($"Compressed index not found at: {indexPath}");
return 0;
throw new FileNotFoundException($"Compressed index not found at: {indexPath}");
}
return new FileInfo(indexPath).Length;
}
var path = Path.Combine(_indexBasePath, doc.FolderId);
if (!Directory.Exists(path))
{
_logger.LogWarning($"Index directory not found at: {path}");
return 0;
throw new DirectoryNotFoundException($"Index directory not found at: {path}");
}
return new DirectoryInfo(path).GetFiles().Sum(f => f.Length);
}
public long GetTotalStorageUsage(bool useCompressedSize = true)
{
if (!_meiliSearchProcessManager.IsProcessRunning())
throw new ProcessNotRunningException();
var result = _client.GetIndexAsync("index_bindings").Result.GetDocumentsAsync<Models.Index>(new DocumentsQuery(){Limit = 1000}).Result;
var total = 0L;
foreach (var index in result.Results)
@ -208,6 +222,9 @@ public class IndexManager:IIndexManager
}
public async Task<long> GetIndexStorageUsageAsync(string indexName, bool useCompressedSize = true)
{
if (!_meiliSearchProcessManager.IsProcessRunning())
throw new ProcessNotRunningException();
var doc = _client.GetIndexAsync("index_bindings").Result.GetDocumentAsync<Models.Index>(indexName).Result;
if (doc.IsCompressed)
@ -218,21 +235,22 @@ public class IndexManager:IIndexManager
var indexPath = await GetIndexFilePath(indexName)+".zip";
if (!File.Exists(indexPath))
{
_logger.LogWarning($"Compressed index not found at: {indexPath}");
return 0;
throw new FileNotFoundException($"Compressed index not found at: {indexPath}");
}
return new FileInfo(indexPath).Length;
}
var path = Path.Combine(_indexBasePath, doc.FolderId);
if (!Directory.Exists(path))
{
_logger.LogWarning($"Index directory not found at: {path}");
return 0;
throw new DirectoryNotFoundException($"Index directory not found at: {path}");
}
return new DirectoryInfo(path).GetFiles().Sum(f => f.Length);
}
public async Task<long> GetTotalStorageUsageAsync(bool useCompressedSize = true)
{
if (!_meiliSearchProcessManager.IsProcessRunning())
throw new ProcessNotRunningException();
var result = _client.GetIndexAsync("index_bindings").Result.GetDocumentsAsync<Models.Index>(new DocumentsQuery(){Limit = 1000}).Result;
var total = 0L;
foreach (var index in result.Results)
@ -278,8 +296,7 @@ public class IndexManager:IIndexManager
var indexPath = await GetIndexFilePath(indexName);
if (!Directory.Exists(indexPath))
{
_logger.LogWarning($"Index directory not found at: {indexPath}");
return;
throw new DirectoryNotFoundException($"Index directory not found at: {indexPath}");
}
var compressedPath = indexPath + ".zip";
@ -322,10 +339,9 @@ public class IndexManager:IIndexManager
}
catch (Exception ex)
{
_logger.LogError($"Failed to compress index '{indexName}': {ex.Message}");
throw;
throw new IndexCompressionException(indexName, "compress", ex);
}
_processManager.StopProcess();
_meiliSearchProcessManager.StopProcess();
}
private async Task DecompressIndex(string indexName)
@ -336,8 +352,7 @@ public class IndexManager:IIndexManager
if (!File.Exists(compressedPath))
{
_logger.LogWarning($"Compressed index not found at: {compressedPath}");
return;
throw new FileNotFoundException($"Compressed index not found at: {compressedPath}");
}
_logger.LogTrace($"Decompressing index '{indexName}' to {extractPath}...");
@ -379,8 +394,7 @@ public class IndexManager:IIndexManager
}
catch (Exception ex)
{
_logger.LogError($"Failed to decompress index '{indexName}': {ex.Message}");
throw;
throw new IndexCompressionException(indexName, "decompress", ex);
}
}
#endregion

View File

@ -1,4 +1,5 @@
using System.Diagnostics;
using meilisearch.NET.Exceptions;
using Microsoft.Extensions.Logging;
namespace meilisearch.NET.Services.ProcessManagement;
@ -35,14 +36,20 @@ public abstract class BaseProcessManager : IProcessManager
}
catch (Exception ex)
{
Logger.LogError($"Failed to start {GetProcessName()}: {ex.Message}");
throw;
throw new ProcessStartException(ex,ex.Message);
}
}
public virtual void StopProcess()
{
Process?.Kill();
try
{
Process?.Kill();
}
catch (Exception ex)
{
Logger.LogError($"Error stopping {GetProcessName()} process: {ex.Message}", ex);
}
}
public virtual bool IsProcessRunning()