Public API for Favorites

Business logic

The main purpose of this service is to replicate the existing functionality of MYM and MYT endpoints in terms of new 'Favourite service'. This will decrease the load on the 'Master Service'.

New component Favorites API will provide information about matches(events) and teams(participants) for mobile and web LS clients.

_The main advantage is to load and store a full offer in memory

The client response is created only from in-memory data, it allow to avoid slow database queries and minimises response times. Responses are created in special serializer classes

Request restrictions:

  data:
   request:
    max-teams-per-request: 50
    max-events-per-request: 200
   eventsForSpecificTeam:
    teamMaxEvent: 3
    matchesRemainTime: 12h

OpenAPI specification

Public endpoints

My matches

/v1/api/app/mym/[sport]?eventIds=[eventId]&teamIds=[teamId]

Returns information of favorite matches. If present one event, returns one event information.

What events will return, if parameter 'eventIds' has more events, that parameter 'limit'? Before limitation all requested events are sorted by their start times (earlier on top). So 'limit' parameter will limit the latest events by startTime. But on client’s side "old" events are filtered (that have finished later that 48h from now). So, user cannot see events, that older than 48h. Note: When user starts scrolling results, a new request will be sent with increased parameter 'offset', so he will see next page with events, that were limited before.

If present one team, returns three (configured by teamMaxEvent property in application.yml) events and if present one event with team, returns four event.

What events are selected by teamId ?

  1. Get all events by teamId (this step optimized in method TeamEventAggregator.computeAndCachePotentiallyTeamEventIds)

  2. Exclude eventIds received from request

  3. Filter events by status (NOT_STARTED, IN_PROGRESS, INTERRUPTED, FINISHED)

  4. Filter by TimeRange: now < event.scheduledStartDateTime + data.eventsForSpecificTeam.matchesRemainTime (48h)

  5. Sort events: by ScheduledStartDateTimeUTC, ParticipantNames (if ScheduledStartDateTimes are equels)

  6. Limit event count: data.eventsForSpecificTeam.teamMaxEvent (3)

  7. Present stage sorted by positionScores and if they has the same positionsScores then thats sorts by positionTeams

  8. In the stages scope, we sorts by StartDateTime or, if StartDateTime matches, then sorts by "name"

My team

/v1/api/app/myt/[sport]?teamIds=[teamId]

Return information of favorite teams

sport: "soccer", "tennis", "hockey", "basketball", "cricket", "horses";

Internal infrastructure

Favorites system architecture

Internal models

Handle next internal models: participant, stage, season, event, region, categorise. Internal models retrieved from kafka topics are written to Internal cache.

Kafka

Listening six topics: 'favorites-api-events', 'favorites-api-participants', 'favorites-api-stages', 'favorites-api-seasons', 'favorites-api-regions', 'favorites-api-categories'.

Cache

Recording the resulting models in six storage caches: participant, stage, season, event, region, categories.

Startup

On application startup firstly we try to load all records from mongodb and save received messages from next kafka topics ('favorites-api-events', 'favorites-api-participants', 'favorites-api-stages', 'favorites-api-seasons', 'favorites-api-regions', 'favorites-api-categories') to cache

Internal cache

Loaded from DB (MongoDB) and stored in ConcurrentMap. It’s thread-safety map with high throughput under high concurrency.

CDN cache

Cache lifetime is set in application config file 'cache-control:':

mym-max-age-seconds: 10
myt-max-age-seconds: 3600

Time presented in seconds.

VM instance configuration recommendations

CPU: 2 cores, RAM: 6 Gb, HDD: 30 Gb

Metrics

Grafana dashboard: 'Favorites prometheus monitoring'.

There are 3 types of metrics: GAUGE, COUNTER, TIMER (contains 2 dimensions: count and sum)

Custom metrics

cache_size (GAUGE)

cache capacity with internal Model. Tags: name

invalid_model_total (COUNTER)

total count of invalid records(models) received from mongo/kafka. Tags: model

model_updates_total (COUNTER)

total count of handled messages, received from kafka. Tags: model, operation

Predefined metrics

jetty.*

metrics show the state of jetty thread pool.

http_server_requests_seconds.*

displays how the api is used. Tags: exception, method, outcome, status, uri

spring_kafka_listener_seconds.*

displays information on kafka connection and consumption. Tags: exception, name, result

Test REST API Endpoints

Health

Default Actuator endpoint with custom data that specify is caches was initial loaded.

Request example

Unresolved directive in index.adoc - include::{snippets}/health/http-request.adoc[]

Response example

Unresolved directive in index.adoc - include::{snippets}/health/http-response.adoc[]

Load testing scenarios

Scaling policy recommendations

If the CPU load is more than 70%, then we launch the second node