Skip to content

Firefly: send to flow destination and optional collector#8040

Merged
kofemann merged 2 commits intodCache:11.2from
ShawnMcKee:feat/firefly-collector-destinations-11.2.2
Mar 13, 2026
Merged

Firefly: send to flow destination and optional collector#8040
kofemann merged 2 commits intodCache:11.2from
ShawnMcKee:feat/firefly-collector-destinations-11.2.2

Conversation

@ShawnMcKee
Copy link
Contributor

Summary

This PR updates firefly sending semantics to use existing configuration variables while preserving default behavior.

  • pool.enable.firefly=true: continue sending fireflies to the transfer flow destination (UDP 10514).
  • pool.firefly.destination unset: no extra copy is sent.
  • pool.firefly.destination set: send one additional copy to that destination.

The configured pool.firefly.destination accepts:

  • host (defaults to UDP 10514)
  • host:port (explicit port override)

Code changes

  • modules/dcache/src/main/java/org/dcache/pool/movers/TransferLifeCycle.java
    • Add optional collector destination field.
    • Keep primary flow-destination send behavior.
    • Send an additional copy to configured collector when set.
    • Avoid duplicate send when collector equals primary destination.
    • Document setFireflyDestination semantics and accepted formats.
  • skel/share/defaults/pool.properties
    • Update comments for pool.firefly.destination to describe new behavior and examples.

Build validation

Executed:

  • mvn -pl modules/dcache -am -DskipTests compile
  • mvn -pl modules/dcache -am -DskipTests package

Result:

  • BUILD SUCCESS

Runtime validation (UMFS19 + dcdum01)

Deployed updated dcache-core-11.2.1.jar on umfs19 and restarted:

  • dcache@umfs19Domain_1.service active
  • dcache@umfs19Domain_2.service active

Runtime config used:

  • pool.enable.firefly=true
  • pool.firefly.destination=firefly-collector.cern.ch

Deterministic protocol tests:

  • xrootd with scitag.flow=313
  • WebDAV with SciTag: 313

Capture (/tmp/firefly-collector-test.pcap) showed destinations:

  • flow destination: 2001:48a8:68f7:1:192:41:231:128:10514
  • collector destination: 2001:1458:301:86::100:8:10514 (firefly-collector.cern.ch)

Counts by protocol/state (aggregated across both destinations):

  • xrootd start: 2
  • xrootd end: 2
  • https start: 2
  • https end: 2

Per destination each protocol/state appeared exactly once, confirming dual-send:

  • one copy to flow destination
  • one copy to configured collector

Signed-off-by: Shawn McKee <smckee@umich.edu>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates pool “firefly” UDP marker sending so the default behavior (send to transfer flow destination/UDP 10514) remains unchanged while allowing an optional second copy to be sent to a separately configured collector.

Changes:

  • Add an optional “collector” destination and send firefly markers to both primary destination and collector (when configured and different).
  • Keep the primary destination logic unchanged; avoid sending a duplicate when collector equals primary.
  • Update pool.firefly.destination documentation to describe the new “additional collector copy” behavior and provide examples.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
modules/dcache/src/main/java/org/dcache/pool/movers/TransferLifeCycle.java Adds optional collector destination and routes start/end markers to primary + optional collector.
skel/share/defaults/pool.properties Updates property comments to document new pool.firefly.destination semantics and examples.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +243 to +254
* Send flow marker to the primary destination and optional configured collector.
*
* @param primaryDst Primary destination (based on flow/peer)
* @param payload the marker
*/
private void sendToMultipleDestinations(InetSocketAddress primaryDst, @Nonnull String payload) {
// Send to primary destination
send(primaryDst, payload);

// Send to collector if configured and different from primary
if (fireflyCollector != null && !fireflyCollector.equals(primaryDst)) {
send(fireflyCollector, payload);
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the new dual-send behavior, sendToMultipleDestinations may call send twice per marker, and send currently creates a new DatagramSocket each time. When a collector is configured, this doubles socket creation overhead for every start/end marker. Consider reusing a single socket per marker (send to primary and collector from the same socket) to reduce per-transfer overhead.

Suggested change
* Send flow marker to the primary destination and optional configured collector.
*
* @param primaryDst Primary destination (based on flow/peer)
* @param payload the marker
*/
private void sendToMultipleDestinations(InetSocketAddress primaryDst, @Nonnull String payload) {
// Send to primary destination
send(primaryDst, payload);
// Send to collector if configured and different from primary
if (fireflyCollector != null && !fireflyCollector.equals(primaryDst)) {
send(fireflyCollector, payload);
* Send flow marker using an existing datagram socket.
*
* @param socket Datagram socket to use for sending.
* @param dst Inet address where the flow marker should be sent.
* @param payload the marker
* @throws IllegalStateException if flow marker ist not build.
*/
private void send(DatagramSocket socket, InetSocketAddress dst, @Nonnull String payload)
throws IllegalStateException {
byte[] data = payload.getBytes(StandardCharsets.UTF_8);
DatagramPacket p = new DatagramPacket(data, data.length, dst);
try {
socket.send(p);
} catch (IOException e) {
LOGGER.warn("Failed to send flow marker to {}: {}", dst, e.getMessage());
}
}
/**
* Send flow marker to the primary destination and optional configured collector.
*
* @param primaryDst Primary destination (based on flow/peer)
* @param payload the marker
*/
private void sendToMultipleDestinations(InetSocketAddress primaryDst, @Nonnull String payload) {
try (DatagramSocket socket = new DatagramSocket()) {
// Send to primary destination
send(socket, primaryDst, payload);
// Send to collector if configured and different from primary
if (fireflyCollector != null && !fireflyCollector.equals(primaryDst)) {
send(socket, fireflyCollector, payload);
}
} catch (IOException e) {
LOGGER.warn("Failed to send flow marker: {}", e.getMessage());

Copilot uses AI. Check for mistakes.
@dCache dCache deleted a comment from Copilot AI Mar 12, 2026
private Predicate<InetAddress> localSubnet = a -> false;

// optional additional collector destination for firefly markers
private InetSocketAddress fireflyCollector = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null is default initializer

* If no port is provided, the default firefly UDP port (10514) is used.
*/
public void setFireflyDestination(String addr) {
fireflyCollector = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed.

@dCache dCache deleted a comment from Copilot AI Mar 12, 2026
- Drop redundant '= null' field initializer
- Remove unused Strings import; use addr.isBlank() instead
- Extend setFireflyDestination Javadoc with IPv6 bracket format examples
- Reuse single DatagramSocket for primary + collector sends

Signed-off-by: Shawn McKee <smckee@umich.edu>
Copy link
Contributor Author

@ShawnMcKee ShawnMcKee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestions! Addressed all comments in commit 9f4b114ed6:

  • fireflyCollector = null — dropped the explicit = null field initializer (default for object references).
  • fireflyCollector = null; inside setter — removed the redundant reset line at the top of setFireflyDestination.
  • Blank-string guard — replaced Strings.isNullOrEmpty(addr) with addr != null && !addr.isBlank() to correctly handle whitespace-only strings; also removed the now-unused Strings import.
  • IPv6 Javadoc — added [ipv6-address] and [ipv6-address]:port as documented accepted formats, with a note that the bracketed form is required by HostAndPort.fromString when a port is present.
  • Socket reuse — refactored send to accept a caller-supplied DatagramSocket; sendToMultipleDestinations now opens one socket and uses it for both the primary and collector sends, halving socket creation overhead when a collector is configured.

Copy link
Member

@DmitryLitvintsev DmitryLitvintsev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. I will also wait for our colleagues in Europe to have a look as well tomorrow.

@kofemann
Copy link
Member

@ShawnMcKee, thanks for the contribution.

@kofemann kofemann merged commit 019bc90 into dCache:11.2 Mar 13, 2026
1 check passed
@kofemann
Copy link
Member

The PR is against 11.2. Will the update to master contain the same changes or should I cherry-pick them myself?

@ShawnMcKee
Copy link
Contributor Author

Can you cherry-pick them? Then we can delete the branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants