Loading…
Loading…
Manages the lifecycle and persistence of service entities, including creation, updates, deletion, and configuration for routing and enforcement.
The Service Repository is a data persistence component responsible for all database operations related to service entities. It provides a programmatic interface for creating, reading, updating, and deleting services and their associated configurations. This component sits between the administrative API logic and the underlying database, encapsulating all SQL queries and transaction management for service objects.
The repository manages the core s2r_service table and interacts with related tables for service versions, runtime routes, and backend profiles. Its primary role is to ensure data integrity and to abstract the details of the database schema from the rest of the application. It is called by higher-level administrative services to manage the complete lifecycle of an API service definition on the platform.
Key responsibilities include:
The Service Repository uses a JdbcTemplate to execute raw SQL queries against the database. All operations are tenant-scoped, ensuring that one tenant cannot access or modify another tenant's service data.
A service is represented by the ServiceRecord object, which aggregates data from several database tables. The primary table is s2r_service.
When retrieving a service, the repository joins s2r_service with two key subqueries:
VERSION_STATE_JOIN_SQL): A subquery on s2r_service_version that calculates the latest active version number (active_version_no) and the latest draft version number (draft_version_no) for each service.USAGE_JOIN_SQL): A subquery on s2r_runtime_agg_service_hourly that finds the most recent timestamp (last_api_usage_at) a service was used in a given environment.This composition provides a complete view of a service's definition, versioning state, and recent activity in a single query.
When a new service is created via the create method, a new record is inserted into the s2r_service table.
UUID is generated for the service ID.status defaults to draft.enforcement_mode defaults to enforce to provide strict validation for new services out of the box.direction is not specified, it defaults to PUBLISH.The update method modifies an existing service record. It coalesces new values with existing ones, allowing for partial updates.
A critical function is syncRuntimeRouteState, which is triggered after a service's status is changed via update or deprecate. This method ensures the runtime routing table (s2r_runtime_route) reflects the service's current state:
disabled or deprecated, all runtime routes for that service are immediately disabled (enabled = false).o.enabled = true) on the service's active version (sv.is_active = true).
c. For each enabled operation, a RouteSeed is created.
d. A RouteCollisionGuard checks if any of the new route paths are already in use by a different service. If a collision is detected, the operation fails.
e. If no collisions exist, the repository "upserts" the routes into s2r_runtime_route. An INSERT ... ON CONFLICT statement claims the route path for this service, setting enabled = true.GATEWAY ServicesServices with direction set to GATEWAY have special handling for both updates and route synchronization:
lockEnvironmentIfGateway). This prevents race conditions with other processes that might modify routing declarations for the same environment.GATEWAY services expose a single endpoint (POST /{environment}/{serviceKey}) regardless of how many operations are defined. The collapseGatewaySeeds method transforms the list of per-operation RouteSeed objects into a single seed for the service's canonical gateway endpoint. The operation_id from the first enabled operation (ordered by operation_key) is used to satisfy schema constraints, but it does not affect routing.The hardDelete method permanently removes a service and its dependent configuration from the database. It is designed to be a safe and bounded operation.
WARNING
An active service cannot be hard-deleted. The service must first be moved to a non-active status like disabled or deprecated.
The deletion process follows these steps:
UPDATE and DELETE operations to remove dependencies that could cause foreign key violations or long-running transactions. This includes:
service_id in s2r_alert_mail_history.s2r_alert_state, s2r_service_alert_config, and s2r_service_synthetic_monitor.default_backend_profile_id on the s2r_service itself.s2r_consumer_ip_grant records.s2r_runtime_route, s2r_openapi_artifact, s2r_backend_profile, s2r_operation, and s2r_service_version.s2r_service table.IMPORTANT
The hardDelete operation does not delete historical traffic and observability data from tables like s2r_traffic_log or s2r_observed_traffic_hourly. This data is considered an immutable record of past events and is retained until it ages out naturally.
The entire deletion sequence is wrapped in a retry loop that can execute up to HARD_DELETE_MAX_ATTEMPTS (5) times. This makes the operation resilient to transient database lock contention (PessimisticLockingFailureException).
flowchart TD
subgraph hardDelete(serviceId)
A[Find service by ID] --> B{Service 'active'?};
B -- Yes --> C[Throw IllegalStateException];
B -- No --> D[Start retry loop, attempt=1];
D --> E[Execute cascade of DELETE statements];
E --> F{PessimisticLockingFailureException?};
F -- No --> G[Return HardDeleteResult];
F -- Yes --> H{Attempts < 5?};
H -- Yes --> I[Log, backoff, increment attempt];
I --> E;
H -- No --> J[Log final error, re-throw exception];
endThe following public methods are available on the ServiceRepository class.
| Method | Description |
|---|---|
findAll() | Retrieves all services for the current tenant, ordered by creation date descending. |
findPage(...) | Retrieves a paginated list of services with support for searching and filtering. |
findById(serviceId) | Retrieves a single service by its UUID. Returns an Optional<ServiceRecord>. |
create(...) | Creates a new service with the specified properties and returns the newly created ServiceRecord. |
update(...) | Updates an existing service's properties. Returns the updated ServiceRecord. |
deprecate(serviceId, updatedBy) | Sets a service's status to deprecated and disables its runtime routes. |
hardDelete(serviceId) | Permanently deletes a non-active service and its associated configuration. Returns a HardDeleteResult with counts of deleted items. |
updateEnforcementMode(...) | Updates the request schema enforcement mode for a service. |
updateResponseEnforcementMode(...) | Updates the response schema enforcement mode for a service. |
updateTrafficCapture(...) | Enables or disables live traffic capture for a service and sets the number of transactions to capture. |
hasPublishedVersion(serviceId) | Checks if a service has at least one active version. Returns a boolean. |
create MethodCreates a new service record.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
serviceKey | String | Yes | The unique key for the service. |
serviceName | String | Yes | The human-readable name of the service. |
environment | String | Yes | The environment this service belongs to. |
direction | ServiceDirection | No | The service direction. Defaults to PUBLISH. |
createdBy | String | Yes | The identifier of the user or system performing the action. |
Returns: The full ServiceRecord of the newly created service.
Example SQL Execution: The method executes a SQL statement similar to the following:
insert into s2r_service (id, service_key, service_name, environment, status, direction, debug_enabled, description, enforcement_mode, created_at, updated_at, created_by, updated_by)
values (?, ?, ?, ?, 'draft', ?, false, null, 'enforce', now(), now(), ?, ?)
hardDelete MethodPermanently deletes a service.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
serviceId | UUID | Yes | The ID of the service to delete. |
Returns: A HardDeleteResult record containing the ID and key of the deleted service, a boolean deleted flag, and counts of associated records that were removed.
HardDeleteResult Fields:
| Field | Type | Description |
|---|---|---|
serviceId | UUID | The ID of the service that was targeted for deletion. |
serviceKey | String | The service key of the targeted service. |
deleted | boolean | true if the s2r_service record was deleted. |
runtimeRoutesDeleted | int | The number of s2r_runtime_route records deleted. |
operationsDeleted | int | The number of s2r_operation records deleted. |
versionsDeleted | int | The number of s2r_service_version records deleted. |
backendProfilesDeleted | int | The number of s2r_backend_profile records deleted. |
openApiArtifactsDeleted | int | The number of s2r_openapi_artifact records deleted. |
The following table describes the properties of a service, which correspond to columns in the s2r_service table and joined data.
| Property | Type | Default | Description |
|---|---|---|---|
id | UUID | (generated) | The unique identifier for the service. |
service_key | String | — | A unique, machine-readable key for the service. |
service_name | String | — | A human-readable name for the service. |
environment | String | — | The deployment environment (e.g., 'dev', 'prod'). |
status | String | draft | The lifecycle status. Allowed values: draft, active, disabled, deprecated. |
direction | ServiceDirection | PUBLISH | The service type. Determines routing logic. |
debug_enabled | boolean | false | If true, enables extended debugging features for the service. |
description | String | null | A user-provided description of the service. |
backend_routing_mode | String | single | The routing strategy for multiple backends. Allowed: single, active_passive, round_robin. |
backend_health_check_mode | String | none | The health check method for backends. Allowed: none, ping, tcp, wsdl_fetch. |
backend_health_check_interval_sec | int | 30 | The interval in seconds between health checks. Range: 1-3600. |
backend_health_check_timeout_ms | int | 2000 | The timeout in milliseconds for a health check. Range: 100-120000. |
enforcement_mode | String | enforce | The request schema validation mode. Allowed: off, monitor, enforce. The value shadow is accepted as an alias for monitor. |
response_enforcement_mode | String | monitor | The response schema validation mode. Allowed: off, monitor, enforce. |
traffic_capture_enabled | boolean | false | If true, enables live traffic sample capture. |
traffic_capture_remaining | int | 0 | The number of traffic samples remaining to be captured. Max: 50. |
active_version_no | Integer | null | The version number of the currently active service version. (Read-only) |
draft_version_no | Integer | null | The version number of the latest draft service version. (Read-only) |
last_api_usage_at | OffsetDateTime | null | Timestamp of the last recorded API call for this service. (Read-only) |
created_at | OffsetDateTime | (db now()) | Timestamp of service creation. |
updated_at | OffsetDateTime | (db now()) | Timestamp of the last service update. |
PessimisticLockingFailureException on hardDeletehardDelete operation encountered a database lock on one of the tables it needs to modify, and the lock could not be acquired. This is often due to concurrent operations, such as high-volume traffic logging or another administrative action.hardDelete method will attempt the deletion up to 5 times with an increasing backoff period. If all attempts fail, the exception is re-thrown, and the final failure is logged. The user may need to retry the operation later during a period of lower system load.IllegalArgumentExceptionThis exception is thrown when input validation fails. Common causes include:
status, enforcement_mode, response_enforcement_mode, backend_routing_mode, or backend_health_check_mode.backend_health_check_interval_sec (must be 1-3600) or backend_health_check_timeout_ms (must be 100-120000).traffic_capture_remaining (must be 0-50).IllegalStateExceptionThis exception indicates an invalid state for an operation.
hardDelete if the target service has a status of active. The service must be deactivated first.The syncRuntimeRouteState method internally uses a RouteCollisionGuard. If an attempt to activate a service version would create a route (e.g., POST /my/path) that is already active for a different service in the same environment, the operation will fail to prevent ambiguity in routing. The conflicting operation on the new service version must be disabled or its path changed.
create, update, deprecate, etc.) are scoped by tenant using SQL fragments from the AuditRepository.GATEWAY services, the advisory lock taken during updates serializes operations against the archival process for trusted proxy declarations, managed by the TrustedProxyDeclarationAdminRepository.