Loading…
Loading…
Describes the aggregation process that collapses fine-grained traffic data into canonical service definitions for Master Discovery.
The MasterDiscoveryAggregator is a pure-function component that transforms fine-grained, per-request traffic data into a consolidated, service-centric view. It serves as a central processing step in the Master Discovery pipeline.
The primary problem this component solves is the reconciliation of raw traffic data, which is often fragmented and noisy. A single logical service may appear as dozens of distinct rows in the raw data, varying by consumer IP, specific gateway virtual server, or minor URI differences. The aggregator's purpose is to collapse these disparate rows into a single, canonical entry for each unique service within each environment.
It takes a list of flat data rows as input, where each row represents an observed traffic event with details like source IP, virtual servers, and backend pools. Its output is a refined list of aggregated service definition rows. Each output row represents a unique service, with consolidated lists of consumers, infrastructure components, and summed traffic metrics over various time windows. This aggregation is critical for presenting a clean and accurate inventory of discovered services.
The aggregation process is executed in a multi-phase pipeline. Each phase refines the data, culminating in a list of canonical service definitions. The primary entry point is the collapseByUriAndEnvironment method.
flowchart TD
subgraph Input
A[Raw Traffic Rows]
end
subgraph Aggregation Pipeline
B(Phase A: Per-Row Environment Detection) --> C(Phase B: Hostname-Diff Fallback)
C --> D(Phase C: Bucket by Service Key)
D --> E(Phase D: Merge by Backend URL)
E --> F(Phase E: Filter Non-Service Rows)
end
subgraph Output
G[Aggregated Service Rows]
end
A --> B
F --> GThe first phase processes each raw input row individually to determine its canonical URI and environment.
serviceUri is canonicalized by lower-casing and stripping query strings and .svc suffixes. This ensures that minor variations of a URI are treated as the same service. Rows with empty or root (/) URIs are discarded.frontVirtualServer, backVirtualServer, dataPowerDomainName, and pool member hostnames, are collected. These act as signals for environment detection.EnvironmentDetector service uses these signals to determine the most likely environment (e.g., "Prod", "Test").environment field on the raw row itself. This field, often pre-populated by upstream traffic rollup processes, is considered a more reliable source for traffic from generic gateways (e.g., HAProxy, NGINX) that may lack environment-specific naming conventions in their infrastructure signals. This fallback is skipped if the rollup environment is a placeholder value like auto._canonicalUri, _env (environment), _envSource (detection source), and _envIssues—are temporarily added to the raw row map for use in subsequent phases.This phase provides a secondary environment detection mechanism for ambiguous cases. It operates on groups of rows that share the same canonical URI.
If a group of rows contains at least one row whose environment was unresolved or assigned a default, this phase attempts to find a more specific environment by comparing backend hostnames. It extracts hostnames from the serviceBackendUrl or backPoolMembers fields of all rows in the group. If distinct hostnames are found, the HostnameDiffEnvDetector analyzes their differences to infer environments. If a more specific environment is found, the row's temporary _env and _envSource fields are updated in place.
This is the core aggregation step where individual rows are collapsed into buckets. Each bucket represents a single, unique discovered service.
A composite key is generated for each row: _canonicalUri + | + _env + | + backendKey.
The backendKey is a crucial discriminator that prevents services with different backends from being incorrectly merged. It is determined using the following precedence:
serviceBackendUrl.serviceBackendKey (a pre-computed hash), if the URL is blank.frontVirtualServer name, as a final fallback.Rows are placed into buckets based on this composite key. This ensures that two services with the same URI and in the same environment but routing to different backends (e.g., a blue/green deployment) are kept as distinct entries.
Within each bucket, traffic statistics (hits1y, hits1m, etc.) are summed, and infrastructure components (frontVirtualServers, consumers, backPoolMembers, etc.) are collected into unique sets.
This phase acts as a safety net to merge service entries that were separated into different environment buckets in Phase C but are discovered to point to the exact same backend. This can happen during migrations or due to inconsistent environment naming in gateway configurations.
serviceUriCanonical + | + serviceBackendUrl. Rows with a blank URI or backend are not merged.hits1m (1-month hit count) is chosen for the final merged row.environment_mismatch discovery issues are removed, as the ambiguity has been resolved by this phase.The final phase removes "phantom" rows that represent noise rather than actual, integrable services. Two types of rows are targeted for removal:
Context Root Placeholders: A row is identified as a placeholder (e.g., a request to a base application path like /MyApplication instead of a service endpoint like /MyApplication/v1/service) and removed if it meets all of the following criteria:
hits1y) is negligible compared to the total traffic of its descendant services (specifically, less than 1%).Operations in Service Slots: A row is removed if it appears to be a misdirected call where a SOAP operation name was used in the service part of the URI. This occurs if all of the following conditions are met:
knownOperationNames set).Rows that survive this filtering phase constitute the final list of aggregated services.
The aggregator is exposed as a class with static methods.
MasterDiscoveryAggregator.collapseByUriAndEnvironmentThis method orchestrates the entire aggregation pipeline.
Parameters
| Name | Type | Description |
|---|---|---|
rawRows | List<Map<String, Object>> | A list of fine-grained traffic data maps, typically produced by F5LearningRepository.computeMasterDiscoveryLive. See Input Row Fields for expected keys. |
environments | List<EnvironmentDefinition> | A list of all configured environment definitions for the platform. Used by the EnvironmentDetector and HostnameDiffEnvDetector. |
knownOperationNames | Set<String> | A set of all known SOAP operation names, lower-cased. This is used in Phase E to identify and filter out rows where an operation name was mistakenly used as a service URI. Pass an empty set to disable this check. |
Returns
A List<Map<String, Object>> containing the aggregated and collapsed service rows. Each map in the list represents a single canonical service. See Output Row Fields for the structure of these maps.
The rawRows maps are expected to contain the following keys, which are used during aggregation. The source does not specify types or defaults for all fields.
| Field Name | Description |
|---|---|
serviceUri | The display URI of the service. |
serviceUriCanonical | A pre-canonicalized version of the service URI. If present, this is used directly. |
frontVirtualServer | The name of the front-end virtual server that received the traffic. |
backVirtualServer | The name of the back-end virtual server. |
frontPoolMembers | A list of members in the front-end pool. |
backPoolMembers | A list of members in the back-end pool. |
dataPowerDomainName | The name of the DataPower domain. |
dataPowerObjectName | The name of the DataPower object. |
dataPowerInstanceName | The name of the specific DataPower instance. |
dataPowerGatewaysAll | A list of all DataPower instances associated with the domain. |
serviceBackendUrl | The resolved backend URL for the traffic. |
serviceBackendKey | A canonical hash of the backend, used when the URL is not available. |
environment | The environment name from an upstream rollup process. Used as a fallback. |
consumer / clientIp | The IP address of the API consumer. |
hits / hits7d | Traffic counts for a 7-day period. |
hits24h, hits1m, etc. | Traffic counts over various time windows (24 hours, 1 month, etc.). |
lastSeenAt | An OffsetDateTime timestamp of when the traffic was last observed. |
source | A tag indicating the origin of the data. |
Each map in the list returned by collapseByUriAndEnvironment conforms to the following structure, generated by the internal Bucket.toRow() method.
| Field Name | Type | Description |
|---|---|---|
serviceUri | String | The display URI for the service. |
serviceUriCanonical | String | The canonical, lower-cased URI used for grouping. |
environment | String | The resolved environment for the service. |
environmentSource | String | The method by which the environment was determined (e.g., signal:front_vs, rollup, auto:hostname_diff). |
environmentManual | null | Placeholder for manual overrides. Not set by this aggregator. |
serviceBackendUrl | String | The preferred backend URL, chosen from the raw row with the most hits7d. |
serviceBackendKey | String | The canonical key for the backend. |
serviceBackendUrls | List<String> | A list of all unique backend URLs observed for this service. |
serviceBackendType | String | ha_pool if multiple backend URLs exist, single if one, unresolved if none. |
frontVirtualServers | List<String> | A list of unique front-end virtual servers associated with the service. |
frontPoolMembers | List<String> | A list of unique members in the front-end pools. |
dataPowerDomain | String | The associated DataPower domain. |
dataPowerGateways | List<String> | A list of unique DataPower gateway instances. |
dataPowerFrontendUrl | String | The frontend URL from the DataPower configuration. |
dataPowerBackendUrl | String | The backend URL from the DataPower configuration. |
dataPowerRouteMode | String | The route mode from the DataPower configuration. |
dnsResolutionTrail | List | An empty list, intended to be populated by a later processing step. |
backVirtualServers | List<String> | A list of unique back-end virtual servers. |
backPoolMembers | List<String> | A list of unique members in the back-end pools. |
wsdlUrl | String | A WSDL URL, if discovered. |
wsdlUrlSource | String | datapower if the WSDL URL came from DataPower config, observed if from traffic. |
wsdlStatus | String | unknown if a WSDL URL is present, otherwise blank. Intended to be updated by a WSDL probe. |
wsdlLastCheckedAt | null | Placeholder for the WSDL probe timestamp. |
serviceId | null | Placeholder for a service identifier. |
serviceStatus | String | Placeholder for service status. |
consumerCount | Integer | The number of unique consumer IPs observed. |
hits24h, hits7d, etc. | Long | Summed traffic counts over various time windows. |
source | String | A comma-separated string of source tags from the merged raw rows. |
discoveryStatus | String | Placeholder for the final discovery status (e.g., complete, incomplete), set by the caller. |
discoveryIssues | List<Map<String, Object>> | A list of issues detected during environment resolution or other steps. |
lastSeenAt | OffsetDateTime | The most recent timestamp observed across all merged raw rows. |
consumers | List<Map<String, Object>> | A list of child objects, one for each unique consumer IP, with aggregated traffic data. |
A primary design goal of the aggregator is to ensure "topology purity." A service row labeled for one environment (e.g., "Prod") should never contain infrastructure components (like virtual servers or pool members) from another environment (e.g., "Test").
bucketBackendDiscriminator) is the first line of defense. By including a backend-specific key in the bucketing logic, it prevents rows with the same URI but different backends from being merged prematurely.mergeSiblingBackendRow) enforces this rule strictly during cross-environment merges. When two rows with the same backend but different environment labels are merged, the infrastructure topology of the traffic "winner" is kept, and the loser's is discarded entirely. This prevents the final row from having a mixed list of virtual servers or pool members.Operators may notice that some entries from raw traffic logs do not appear in the final discovered services list. This is often the intended result of the filtering in Phase E.
/DigitalRashal18), has no backend, and has negligible traffic compared to its more specific descendant services (e.g., /DigitalRashal18/UpdateMedicalStatus), it will be filtered out. This removes noise and focuses discovery on actual, callable endpoints./services/processCleanlinessLaw instead of /services/CleanlinessLawBean), it will be removed, provided the real service is also present.The environment for a service is determined by a clear order of precedence:
EnvironmentDetector using specific signals from infrastructure names (e.g., a _prod_ token in a virtual server name).environment field on the raw input row, if the primary detection only yielded a default. This honors upstream rollup data, which is crucial for generic gateways.HostnameDiffEnvDetector (Phase B), which infers the environment by comparing backend hostnames of sibling services.ServiceUriCanonicalizer: The component responsible for normalizing service URIs.EnvironmentDetector: The primary service for detecting environments from infrastructure signals.HostnameDiffEnvDetector: The fallback service for detecting environments based on hostname differences.F5LearningRepository: A typical upstream component that provides the raw traffic data for aggregation.