From 43aa7ddc8ce42ff9a5cbbe95ed8334dd05d103d5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Jul 2026 14:54:11 +0000 Subject: [PATCH 01/12] Fix hotfile jobs stuck during initialization --- .../java/org/dcache/pool/migration/Job.java | 58 +++++++++++++++++++ .../pool/migration/MigrationModule.java | 2 +- .../pool/migration/MigrationModuleTest.java | 23 ++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java index b36c19348d4..2339511cb3f 100644 --- a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java +++ b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java @@ -186,6 +186,64 @@ public void start() { } } + /** + * Starts a job with a preselected set of entries, avoiding a full repository scan during + * initialization. + */ + public void start(Iterable entries) { + _lock.lock(); + try { + checkState(_state == State.NEW); + _state = State.INITIALIZING; + + long refreshPeriod = _definition.refreshPeriod; + ScheduledExecutorService executor = _context.getExecutor(); + + _refreshTask = + executor.scheduleWithFixedDelay(new FireAndForgetTask(() -> { + _definition.sourceList.refresh(); + _definition.poolList.refresh(); + }), 0, refreshPeriod, TimeUnit.MILLISECONDS); + } finally { + _lock.unlock(); + } + + try { + _context.getRepository().addListener(this); + + _lock.lock(); + try { + for (CacheEntry entry : entries) { + if (accept(entry)) { + add(entry); + } + } + + if (getState() == State.INITIALIZING) { + setState(State.RUNNING); + } + } finally { + _lock.unlock(); + } + } finally { + _lock.lock(); + try { + switch (getState()) { + case INITIALIZING: + setState(State.FAILED); + break; + case CANCELLING: + schedule(); + break; + default: + break; + } + } finally { + _lock.unlock(); + } + } + } + public JobDefinition getDefinition() { return _definition; } diff --git a/modules/dcache/src/main/java/org/dcache/pool/migration/MigrationModule.java b/modules/dcache/src/main/java/org/dcache/pool/migration/MigrationModule.java index ef99476459b..74aadfa1102 100644 --- a/modules/dcache/src/main/java/org/dcache/pool/migration/MigrationModule.java +++ b/modules/dcache/src/main/java/org/dcache/pool/migration/MigrationModule.java @@ -1377,7 +1377,7 @@ public synchronized void reportFileRequest(PnfsId pnfsId, long numberOfRequests, "About to start migration job with id {} for pnfsId {}. Job definition: {}", jobId, pnfsId, job.getDefinition()); try { - job.start(); + job.start(Collections.singletonList(cacheEntry)); LOGGER.debug("Started migration job with id {} for pnfsId {}", jobId, pnfsId); } catch (Exception e) { diff --git a/modules/dcache/src/test/java/org/dcache/pool/migration/MigrationModuleTest.java b/modules/dcache/src/test/java/org/dcache/pool/migration/MigrationModuleTest.java index 5e9b7abb408..1e045cf93b6 100644 --- a/modules/dcache/src/test/java/org/dcache/pool/migration/MigrationModuleTest.java +++ b/modules/dcache/src/test/java/org/dcache/pool/migration/MigrationModuleTest.java @@ -29,6 +29,8 @@ import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import com.google.common.util.concurrent.ListenableFuture; @@ -137,6 +139,27 @@ public void testReportFileRequestAboveThreshold() throws Exception { assertTrue(module.hasJob("hotfile-" + pnfsId)); } + @Test + public void testReportFileRequestDoesNotScanRepositoryToStartHotfileJob() throws Exception { + PnfsId pnfsId = new PnfsId("0000A1B2C3D4E5F7"); + ProtocolInfo protocolInfo = mock(ProtocolInfo.class); + when(protocolInfo.getProtocol()).thenReturn("DCap"); + when(protocolInfo.getMajorVersion()).thenReturn(3); + when(entry.getFileAttributes()).thenReturn(fileAttributes); + when(entry.getLastAccessTime()).thenReturn(0L); + when(entry.getPnfsId()).thenReturn(pnfsId); + when(repository.getEntry(pnfsId)).thenReturn(entry); + + module.setThreshold(0L); + module.reportFileRequest(pnfsId, 1L, protocolInfo); + + verify(repository, never()).iterator(); + + MigrationModule.MigrationInfoCommand cmd = module.new MigrationInfoCommand(); + cmd.id = "hotfile-" + pnfsId; + assertFalse(cmd.call().contains("State : INITIALIZING")); + } + @Test public void testHotfileJobHousekeeping() throws Exception { SerializablePoolMonitor poolMonitor = mock(SerializablePoolMonitor.class); From 542eaacd019eaf7b561f5e0a6b9d5d2311a10553 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Jul 2026 14:55:15 +0000 Subject: [PATCH 02/12] Refine hotfile initialization fix --- .../java/org/dcache/pool/migration/Job.java | 126 ++++++++---------- .../pool/migration/MigrationModuleTest.java | 5 +- 2 files changed, 54 insertions(+), 77 deletions(-) diff --git a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java index 2339511cb3f..9617056ab7c 100644 --- a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java +++ b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java @@ -136,54 +136,26 @@ public Job(MigrationContext context, JobDefinition definition) { } public void start() { - _lock.lock(); - try { - checkState(_state == State.NEW); - _state = State.INITIALIZING; - - long refreshPeriod = _definition.refreshPeriod; - ScheduledExecutorService executor = _context.getExecutor(); - - _refreshTask = - executor.scheduleWithFixedDelay(new FireAndForgetTask(() -> { - _definition.sourceList.refresh(); - _definition.poolList.refresh(); - }), 0, refreshPeriod, TimeUnit.MILLISECONDS); + beginInitialization(); + _context.getExecutor().submit(new FireAndForgetTask(() -> { + try { + _context.getRepository().addListener(Job.this); + populate(); - executor.submit(new FireAndForgetTask(() -> { + _lock.lock(); try { - _context.getRepository().addListener(Job.this); - populate(); - - _lock.lock(); - try { - if (getState() == State.INITIALIZING) { - setState(State.RUNNING); - } - } finally { - _lock.unlock(); + if (getState() == State.INITIALIZING) { + setState(State.RUNNING); } - } catch (InterruptedException e) { - LOGGER.error("Migration job was interrupted"); } finally { - _lock.lock(); - try { - switch (getState()) { - case INITIALIZING: - setState(State.FAILED); - break; - case CANCELLING: - schedule(); - break; - } - } finally { - _lock.unlock(); - } + _lock.unlock(); } - })); - } finally { - _lock.unlock(); - } + } catch (InterruptedException e) { + LOGGER.error("Migration job was interrupted"); + } finally { + finishInitialization(); + } + })); } /** @@ -191,22 +163,7 @@ public void start() { * initialization. */ public void start(Iterable entries) { - _lock.lock(); - try { - checkState(_state == State.NEW); - _state = State.INITIALIZING; - - long refreshPeriod = _definition.refreshPeriod; - ScheduledExecutorService executor = _context.getExecutor(); - - _refreshTask = - executor.scheduleWithFixedDelay(new FireAndForgetTask(() -> { - _definition.sourceList.refresh(); - _definition.poolList.refresh(); - }), 0, refreshPeriod, TimeUnit.MILLISECONDS); - } finally { - _lock.unlock(); - } + beginInitialization(); try { _context.getRepository().addListener(this); @@ -226,21 +183,44 @@ public void start(Iterable entries) { _lock.unlock(); } } finally { - _lock.lock(); - try { - switch (getState()) { - case INITIALIZING: - setState(State.FAILED); - break; - case CANCELLING: - schedule(); - break; - default: - break; - } - } finally { - _lock.unlock(); + finishInitialization(); + } + } + + private void beginInitialization() { + _lock.lock(); + try { + checkState(_state == State.NEW); + + long refreshPeriod = _definition.refreshPeriod; + ScheduledExecutorService executor = _context.getExecutor(); + + _refreshTask = + executor.scheduleWithFixedDelay(new FireAndForgetTask(() -> { + _definition.sourceList.refresh(); + _definition.poolList.refresh(); + }), 0, refreshPeriod, TimeUnit.MILLISECONDS); + _state = State.INITIALIZING; + } finally { + _lock.unlock(); + } + } + + private void finishInitialization() { + _lock.lock(); + try { + switch (getState()) { + case INITIALIZING: + setState(State.FAILED); + break; + case CANCELLING: + schedule(); + break; + default: + break; } + } finally { + _lock.unlock(); } } diff --git a/modules/dcache/src/test/java/org/dcache/pool/migration/MigrationModuleTest.java b/modules/dcache/src/test/java/org/dcache/pool/migration/MigrationModuleTest.java index 1e045cf93b6..6f21bb67d80 100644 --- a/modules/dcache/src/test/java/org/dcache/pool/migration/MigrationModuleTest.java +++ b/modules/dcache/src/test/java/org/dcache/pool/migration/MigrationModuleTest.java @@ -154,10 +154,7 @@ public void testReportFileRequestDoesNotScanRepositoryToStartHotfileJob() throws module.reportFileRequest(pnfsId, 1L, protocolInfo); verify(repository, never()).iterator(); - - MigrationModule.MigrationInfoCommand cmd = module.new MigrationInfoCommand(); - cmd.id = "hotfile-" + pnfsId; - assertFalse(cmd.call().contains("State : INITIALIZING")); + assertTrue(module.hasJob("hotfile-" + pnfsId)); } @Test From 3d7a8acbb62fc10bd473c962369ece3310b823e9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Jul 2026 14:56:00 +0000 Subject: [PATCH 03/12] Document hotfile startup helpers --- .../main/java/org/dcache/pool/migration/Job.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java index 9617056ab7c..5778cc7b26b 100644 --- a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java +++ b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java @@ -161,6 +161,9 @@ public void start() { /** * Starts a job with a preselected set of entries, avoiding a full repository scan during * initialization. + * + *

This variant performs the initialization on the calling thread and is intended for + * callers, such as hot-file replication, that already know the exact entries to enqueue. */ public void start(Iterable entries) { beginInitialization(); @@ -187,6 +190,11 @@ public void start(Iterable entries) { } } + /** + * Marks the job as initializing and schedules the periodic source and target pool refresh. + * + *

This method must be called exactly once when transitioning a job out of {@link State#NEW}. + */ private void beginInitialization() { _lock.lock(); try { @@ -206,6 +214,13 @@ private void beginInitialization() { } } + /** + * Completes job initialization bookkeeping. + * + *

If initialization never progressed beyond {@link State#INITIALIZING}, the job is marked + * failed. If cancellation was requested while initializing, scheduling is resumed so the cancel + * path can complete. + */ private void finishInitialization() { _lock.lock(); try { @@ -217,6 +232,7 @@ private void finishInitialization() { schedule(); break; default: + // RUNNING and terminal states do not need any additional cleanup here. break; } } finally { From 25385447a9fc3a4b297691e15499fb4ca38c7863 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Jul 2026 14:56:36 +0000 Subject: [PATCH 04/12] Tidy job initialization cleanup handling --- .../main/java/org/dcache/pool/migration/Job.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java index 5778cc7b26b..c4a00e7b5fe 100644 --- a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java +++ b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java @@ -208,6 +208,7 @@ private void beginInitialization() { _definition.sourceList.refresh(); _definition.poolList.refresh(); }), 0, refreshPeriod, TimeUnit.MILLISECONDS); + _state = State.INITIALIZING; } finally { _lock.unlock(); @@ -231,8 +232,17 @@ private void finishInitialization() { case CANCELLING: schedule(); break; - default: - // RUNNING and terminal states do not need any additional cleanup here. + case NEW: + case RUNNING: + case SLEEPING: + case PAUSED: + case SUSPENDED: + case STOPPING: + case CANCELLED: + case FINISHED: + case FAILED: + // No additional cleanup is needed once initialization has progressed + // beyond INITIALIZING or has already reached a terminal state. break; } } finally { From b86d5021f5c27c2e115a86c93ecb7205702631f0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Jul 2026 14:57:18 +0000 Subject: [PATCH 05/12] Lock hotfile listener registration --- .../main/java/org/dcache/pool/migration/Job.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java index c4a00e7b5fe..c2f071d611b 100644 --- a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java +++ b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java @@ -169,10 +169,10 @@ public void start(Iterable entries) { beginInitialization(); try { - _context.getRepository().addListener(this); - _lock.lock(); try { + _context.getRepository().addListener(this); + for (CacheEntry entry : entries) { if (accept(entry)) { add(entry); @@ -232,15 +232,7 @@ private void finishInitialization() { case CANCELLING: schedule(); break; - case NEW: - case RUNNING: - case SLEEPING: - case PAUSED: - case SUSPENDED: - case STOPPING: - case CANCELLED: - case FINISHED: - case FAILED: + default: // No additional cleanup is needed once initialization has progressed // beyond INITIALIZING or has already reached a terminal state. break; From 4d2bee6608e64ec1102239fc8b53c76b315c0e59 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Jul 2026 14:58:05 +0000 Subject: [PATCH 06/12] Clarify hotfile startup behavior --- .../java/org/dcache/pool/migration/Job.java | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java index c2f071d611b..0ea017d1f2d 100644 --- a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java +++ b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java @@ -163,29 +163,27 @@ public void start() { * initialization. * *

This variant performs the initialization on the calling thread and is intended for - * callers, such as hot-file replication, that already know the exact entries to enqueue. + * callers, such as hot-file replication, that already know the exact entries to enqueue. It + * may therefore block the caller until all provided entries have been evaluated and enqueued. */ public void start(Iterable entries) { beginInitialization(); + _lock.lock(); try { - _lock.lock(); - try { - _context.getRepository().addListener(this); + _context.getRepository().addListener(this); - for (CacheEntry entry : entries) { - if (accept(entry)) { - add(entry); - } + for (CacheEntry entry : entries) { + if (accept(entry)) { + add(entry); } + } - if (getState() == State.INITIALIZING) { - setState(State.RUNNING); - } - } finally { - _lock.unlock(); + if (getState() == State.INITIALIZING) { + setState(State.RUNNING); } } finally { + _lock.unlock(); finishInitialization(); } } @@ -233,8 +231,8 @@ private void finishInitialization() { schedule(); break; default: - // No additional cleanup is needed once initialization has progressed - // beyond INITIALIZING or has already reached a terminal state. + // No additional cleanup is needed once initialization has reached RUNNING, + // SLEEPING, PAUSED, SUSPENDED, STOPPING, CANCELLED, FINISHED, or FAILED. break; } } finally { From 353fb73aec1dbc2da02d03caf035ac23755d56b5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Jul 2026 14:58:48 +0000 Subject: [PATCH 07/12] Align hotfile startup cleanup flow --- .../java/org/dcache/pool/migration/Job.java | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java index 0ea017d1f2d..4347c90805c 100644 --- a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java +++ b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java @@ -163,27 +163,30 @@ public void start() { * initialization. * *

This variant performs the initialization on the calling thread and is intended for - * callers, such as hot-file replication, that already know the exact entries to enqueue. It + * callers, such as hotfile replication, that already know the exact entries to enqueue. It * may therefore block the caller until all provided entries have been evaluated and enqueued. */ public void start(Iterable entries) { beginInitialization(); - _lock.lock(); try { - _context.getRepository().addListener(this); + _lock.lock(); + try { + _context.getRepository().addListener(this); - for (CacheEntry entry : entries) { - if (accept(entry)) { - add(entry); + for (CacheEntry entry : entries) { + if (accept(entry)) { + add(entry); + } } - } - if (getState() == State.INITIALIZING) { - setState(State.RUNNING); + if (getState() == State.INITIALIZING) { + setState(State.RUNNING); + } + } finally { + _lock.unlock(); } } finally { - _lock.unlock(); finishInitialization(); } } From ca7fb1b41bad1ed67bd9d4a8321878cfa2f71175 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Jul 2026 14:59:27 +0000 Subject: [PATCH 08/12] Document async and sync job startup --- .../java/org/dcache/pool/migration/Job.java | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java index 4347c90805c..dc17895a05d 100644 --- a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java +++ b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java @@ -135,6 +135,10 @@ public Job(MigrationContext context, JobDefinition definition) { .getCellName(); } + /** + * Starts a job by asynchronously scanning the repository on the executor and queuing any + * matching entries that are discovered during initialization. + */ public void start() { beginInitialization(); _context.getExecutor().submit(new FireAndForgetTask(() -> { @@ -169,24 +173,21 @@ public void start() { public void start(Iterable entries) { beginInitialization(); + _lock.lock(); try { - _lock.lock(); - try { - _context.getRepository().addListener(this); + _context.getRepository().addListener(this); - for (CacheEntry entry : entries) { - if (accept(entry)) { - add(entry); - } + for (CacheEntry entry : entries) { + if (accept(entry)) { + add(entry); } + } - if (getState() == State.INITIALIZING) { - setState(State.RUNNING); - } - } finally { - _lock.unlock(); + if (getState() == State.INITIALIZING) { + setState(State.RUNNING); } } finally { + _lock.unlock(); finishInitialization(); } } From 5c35ad42d5d8c844cebb3e856fb83b3de90e93db Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Jul 2026 15:00:12 +0000 Subject: [PATCH 09/12] Log synchronous hotfile init failures --- .../main/java/org/dcache/pool/migration/Job.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java index dc17895a05d..283602d3f10 100644 --- a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java +++ b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java @@ -173,8 +173,10 @@ public void start() { public void start(Iterable entries) { beginInitialization(); - _lock.lock(); + boolean locked = false; try { + _lock.lock(); + locked = true; _context.getRepository().addListener(this); for (CacheEntry entry : entries) { @@ -186,8 +188,16 @@ public void start(Iterable entries) { if (getState() == State.INITIALIZING) { setState(State.RUNNING); } + } catch (RuntimeException e) { + LOGGER.error("Migration job initialization failed: {}", e.getMessage(), e); + throw e; + } catch (Error e) { + LOGGER.error("Migration job initialization failed", e); + throw e; } finally { - _lock.unlock(); + if (locked) { + _lock.unlock(); + } finishInitialization(); } } From f9fddb1eb501251ddba6c5b9cd90ef16a2467000 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Jul 2026 15:01:01 +0000 Subject: [PATCH 10/12] Simplify synchronous hotfile startup --- .../main/java/org/dcache/pool/migration/Job.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java index 283602d3f10..dfd12c124d8 100644 --- a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java +++ b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java @@ -173,10 +173,8 @@ public void start() { public void start(Iterable entries) { beginInitialization(); - boolean locked = false; + _lock.lock(); try { - _lock.lock(); - locked = true; _context.getRepository().addListener(this); for (CacheEntry entry : entries) { @@ -188,16 +186,11 @@ public void start(Iterable entries) { if (getState() == State.INITIALIZING) { setState(State.RUNNING); } - } catch (RuntimeException e) { - LOGGER.error("Migration job initialization failed: {}", e.getMessage(), e); - throw e; - } catch (Error e) { + } catch (Throwable e) { LOGGER.error("Migration job initialization failed", e); throw e; } finally { - if (locked) { - _lock.unlock(); - } + _lock.unlock(); finishInitialization(); } } From d04acef324f732e08ef0ea93facb405a25daefe9 Mon Sep 17 00:00:00 2001 From: Chris Green Date: Wed, 8 Jul 2026 11:23:30 -0500 Subject: [PATCH 11/12] pool: improve javadoc for Job.start(Iterable) method Motivation: The javadoc for the synchronous Job.start(Iterable) variant was brief and did not clearly explain the method's purpose, typical callers, or the synchronous blocking behavior. Modification: Expanded the javadoc to clarify that this method bypasses the generic repository scan, is typically used by hot-file replication, and blocks the calling thread until all entries are examined and queued. Added @param and @see tags for better cross-referencing with the asynchronous start() method. Result: Developers reading the code now have a clearer understanding of when and why to use this synchronous startup variant versus the asynchronous one. Target: master Requires-notes: no Requires-book: no --- .../main/java/org/dcache/pool/migration/Job.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java index dfd12c124d8..36696b0c271 100644 --- a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java +++ b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java @@ -163,12 +163,16 @@ public void start() { } /** - * Starts a job with a preselected set of entries, avoiding a full repository scan during - * initialization. + * Starts a job with a pre‑selected set of entries, bypassing the generic repository scan. * - *

This variant performs the initialization on the calling thread and is intended for - * callers, such as hotfile replication, that already know the exact entries to enqueue. It - * may therefore block the caller until all provided entries have been evaluated and enqueued. + *

Typical callers are hot‑file replication, which already knows the exact {@link CacheEntry} + * that triggered replication. The method registers the job as a repository listener, enqueues + * the supplied entries, and then transitions the job to {@link State#RUNNING}. Because the + * work is performed synchronously on the calling thread, the caller blocks until all entries + * have been examined and queued. + * + * @param entries an {@link Iterable} of {@link CacheEntry} objects that should be migrated. + * @see #start() the asynchronous variant that performs a full‑repository scan. */ public void start(Iterable entries) { beginInitialization(); From 5e803810b2b7fed4803ebf593984c454b0ddc80c Mon Sep 17 00:00:00 2001 From: Chris Green Date: Wed, 8 Jul 2026 15:43:20 -0500 Subject: [PATCH 12/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../dcache/src/main/java/org/dcache/pool/migration/Job.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java index 36696b0c271..57009fb168f 100644 --- a/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java +++ b/modules/dcache/src/main/java/org/dcache/pool/migration/Job.java @@ -163,16 +163,16 @@ public void start() { } /** - * Starts a job with a pre‑selected set of entries, bypassing the generic repository scan. + * Starts a job with a pre-selected set of entries, bypassing the generic repository scan. * - *

Typical callers are hot‑file replication, which already knows the exact {@link CacheEntry} + *

Typical callers are hot-file replication, which already knows the exact {@link CacheEntry} * that triggered replication. The method registers the job as a repository listener, enqueues * the supplied entries, and then transitions the job to {@link State#RUNNING}. Because the * work is performed synchronously on the calling thread, the caller blocks until all entries * have been examined and queued. * * @param entries an {@link Iterable} of {@link CacheEntry} objects that should be migrated. - * @see #start() the asynchronous variant that performs a full‑repository scan. + * @see #start() the asynchronous variant that performs a full-repository scan. */ public void start(Iterable entries) { beginInitialization();