SaintAngeLs / distributed_minispace

Microservices-based social network built with .NET8 and ASP .NET Core Blazor WebAssembly, following distributed DDD principles. Scalable social media application CQRS distributed event-driven solution, utilizing SignalR, .NET ML. Features include choreographical Saga patterns, asynchronous messaging, outbox pattern and distributed event sourcing
https://itsharppro.com/case-study/distributed-astravent/
Apache License 2.0
18 stars 4 forks source link

[FEATURE] Improve Event Service Efficiency by Removing Direct Calls to Friends Service in Get Event Handler #427

Open SaintAngeLs opened 1 month ago

SaintAngeLs commented 1 month ago

Description:

Currently, the Event Service is making direct calls to the Friends Service in the GetEventHandler whenever an event is fetched. This process introduces unnecessary latency and potential points of failure, as the entire event retrieval process is dependent on the availability and response time of the Friends Service. This inefficiency affects the performance of the Event Service, especially when handling requests at scale.

Problem:

When the Event Service queries an event, it calls the Friends Service to retrieve the friends data of the user requesting the event. This dependency introduces the following issues:

  1. Latency: The response time of the GetEvent query is extended by the need to call the Friends Service.
  2. Reliability: If the Friends Service is unavailable or experiences high latency, it impacts the Event Service's ability to return the event data in a timely manner.
  3. Unnecessary Overhead: The Friends Service is called for every GetEvent request, even when the friends data is not critical for all use cases.

Proposed Solution:

Refactor the Event Service to decouple the Friends Service calls from the GetEvent query:

  1. Remove Direct Calls: The GetEventHandler should no longer call the Friends Service when retrieving events.
  2. Asynchronous Processing: If friend-related data is necessary (e.g., for showing friends who are attending or interested), this data should be fetched asynchronously and separately from the main event details. This can be handled either by a separate request or during the background processing.
  3. Optional Data: The friends' data (e.g., friends who are signed up or interested in the event) can be retrieved and displayed in a separate request or cached for efficiency, without blocking the main event retrieval.
  4. Improve Efficiency: The Event Service should focus on efficiently retrieving event data without depending on external services during the GetEvent query.

Alternatives Considered:

  1. Caching Friends Data: One alternative was to cache the friends data, but this would add additional complexity in managing cache expiration and synchronization with the actual Friends Service.
  2. Batch Requests: Another alternative was to batch requests to the Friends Service, but this still introduces unnecessary dependencies and may not fully resolve the performance issue.

Additional Context:

SaintAngeLs commented 1 month ago

This issue will also be aligned with the Paralax development. We will have to eliminate possible null refferencial exceptions.

o": null,
  "Pageable": null
}
[20:54:02 INF] Executed endpoint 'HTTP: GET events/search'
[20:54:02 ERR] An error occurred while processing the request.
MongoDB.Driver.Linq.ExpressionNotSupportedException: Expression not supported: Convert(e.Organizer.OrganizationId, Guid).
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToFilterTranslators.ToFilterFieldTranslators.ConvertExpressionToFilterFieldTranslator.Translate(TranslationContext context, UnaryExpression expression)
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToFilterTranslators.ToFilterFieldTranslators.ExpressionToFilterFieldTranslator.Translate(TranslationContext context, Expression expression)
   at MongoDB.Driver.Linq.Linq3Implementation.LinqProviderAdapterV3.TranslateExpressionToField[TDocument,TField](Expression`1 expression, IBsonSerializer`1 documentSerializer, IBsonSerializerRegistry serializerRegistry, Boolean allowScalarValueForArrayField)
   at MongoDB.Driver.ExpressionFieldDefinition`2.Render(RenderArgs`1 args)
   at MongoDB.Driver.SingleItemAsArrayOperatorFilterDefinition`2.Render(RenderArgs`1 args)
   at MongoDB.Driver.AndFilterDefinition`1.Render(RenderArgs`1 args)
   at MongoDB.Driver.AndFilterDefinition`1.Render(RenderArgs`1 args)
   at MongoDB.Driver.PipelineStageDefinitionBuilder.<>c__DisplayClass35_0`1.<Match>b__0(RenderArgs`1 args)
   at MongoDB.Driver.DelegatedPipelineStageDefinition`2.Render(RenderArgs`1 args)
   at MongoDB.Driver.AppendedStagePipelineDefinition`3.Render(RenderArgs`1 args)
   at MongoDB.Driver.AppendedStagePipelineDefinition`3.Render(RenderArgs`1 args)
   at MongoDB.Driver.MongoCollectionImpl`1.AggregateAsync[TResult](IClientSessionHandle session, PipelineDefinition`2 pipeline, AggregateOptions options, CancellationToken cancellationToken)
   at MongoDB.Driver.MongoCollectionImpl`1.UsingImplicitSessionAsync[TResult](Func`2 funcAsync, CancellationToken cancellationToken)
   at MongoDB.Driver.IAsyncCursorSourceExtensions.ToListAsync[TDocument](IAsyncCursorSource`1 source, CancellationToken cancellationToken)
   at MiniSpace.Services.Events.Infrastructure.Mongo.Repositories.Extensions.AggregateByPage[TDocument](IMongoCollection`1 collection, FilterDefinition`1 filterDefinition, SortDefinition`1 sortDefinition, Int32 page, Int32 pageSize) in /home/kaliuser/Documents/portfolio/commercial_apps/distributed_minispace/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/Extensions.cs:line 40
   at MiniSpace.Services.Events.Infrastructure.Mongo.Repositories.EventMongoRepository.BrowseAsync(FilterDefinition`1 filterDefinition, SortDefinition`1 sortDefinition, Int32 pageNumber, Int32 pageSize) in /home/kaliuser/Documents/portfolio/commercial_apps/distributed_minispace/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/EventMongoRepository.cs:line 47
   at MiniSpace.Services.Events.Infrastructure.Mongo.Repositories.EventMongoRepository.BrowseEventsAsync(Int32 pageNumber, Int32 pageSize, String name, String organizer, DateTime dateFrom, DateTime dateTo, Nullable`1 category, Nullable`1 state, IEnumerable`1 organizations, IEnumerable`1 friends, Nullable`1 friendsEngagementType, IEnumerable`1 sortBy, String direction) in /home/kaliuser/Documents/portfolio/commercial_apps/distributed_minispace/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/EventMongoRepository.cs:line 72
   at MiniSpace.Services.Events.Infrastructure.Mongo.Queries.Handlers.GetPaginatedSearchEventsHandler.HandleAsync(GetSearchEvents query, CancellationToken cancellationToken) in /home/kaliuser/Documents/portfolio/commercial_apps/distributed_minispace/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedSearchEventsHandler.cs:line 61
   at Paralax.CQRS.Queries.Dispatchers.QueryDispatcher.QueryAsync[TQuery,TResult](TQuery query, CancellationToken cancellationToken)
   at Paralax.WebApi.CQRS.Builders.DispatcherEndpointsBuilder.<>c__DisplayClass3_0`2.<<Get>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at Paralax.WebApi.EndpointsBuilder.BuildQueryContext[T](HttpContext httpContext, Func`3 context)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|7_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at App.Metrics.AspNetCore.Tracking.Middleware.ApdexMiddleware.MeasureTransaction(HttpContext context)
   at App.Metrics.AspNetCore.Tracking.Middleware.PerRequestTimerMiddleware.TimeTransaction(HttpContext context)
   at App.Metrics.AspNetCore.Tracking.Middleware.RequestTimerMiddleware.TimeTransaction(HttpContext context)
   at App.Metrics.AspNetCore.Tracking.Middleware.ErrorRequestMeterMiddleware.Invoke(HttpContext context)
   at App.Metrics.AspNetCore.Tracking.Middleware.ActiveRequestCounterEndpointMiddleware.Invoke(HttpContext context)
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Paralax.WebApi.Exceptions.ErrorHandlerMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
[20:54:02 INF] Request finished HTTP/1.1 GET http://localhost:5000/events/search?OrganizationId=3be66f89-
DevITSharpPRO commented 1 month ago

@SaintAngeLs, I remember you've mentioned the nullable values in convert query method in Paralax. it has nothing to deal with it. The issue is that the Organizer(Enumerator Type).OrganizationId(Also nullable Type) is compared as the string to Guid.