Skip to content

Commit 755cd92

Browse files
committed
Abort watching on the initial failure
Motivation: If the initial fetch of `Watcher` fails with non-`CentralDogmaException`, it is likely that some configuration is wrong or the file is malformed. So earily completing the initialValueFuture with the exception rather than retrying indefinitely would help users to debug the reason. Modifications: - Complete `initialValueFuture` exceptionally when: - `initialValueFuture` is not completed yet - The cause is not `CentralDogmaException` Result: `Watcher` client stops without retrying if the first attempt fails with a non-`CentralDogmaException`.
1 parent 29250c7 commit 755cd92

2 files changed

Lines changed: 82 additions & 4 deletions

File tree

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright 2025 LINE Corporation
3+
*
4+
* LINE Corporation licenses this file to you under the Apache License,
5+
* version 2.0 (the "License"); you may not use this file except in compliance
6+
* with the License. You may obtain a copy of the License at:
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package com.linecorp.centraldogma.client.armeria;
18+
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
21+
22+
import java.util.concurrent.ExecutionException;
23+
import java.util.concurrent.TimeUnit;
24+
25+
import org.junit.jupiter.api.Test;
26+
import org.junit.jupiter.api.extension.RegisterExtension;
27+
28+
import com.linecorp.centraldogma.client.CentralDogma;
29+
import com.linecorp.centraldogma.client.CentralDogmaRepository;
30+
import com.linecorp.centraldogma.client.Watcher;
31+
import com.linecorp.centraldogma.common.Change;
32+
import com.linecorp.centraldogma.common.Entry;
33+
import com.linecorp.centraldogma.common.PathPattern;
34+
import com.linecorp.centraldogma.testing.junit.CentralDogmaExtension;
35+
36+
class WatchRequestErrorTest {
37+
38+
@RegisterExtension
39+
static final CentralDogmaExtension dogma = new CentralDogmaExtension() {
40+
41+
@Override
42+
protected void scaffold(CentralDogma client) {
43+
client.createProject("foo").join();
44+
final CentralDogmaRepository repo = client.createRepository("foo", "bar").join();
45+
repo.commit("Test", Change.ofTextUpsert("/a.txt", "Hello, world!"))
46+
.push().join();
47+
}
48+
};
49+
50+
@Test
51+
void shouldFailInitialValueFutureForInitialFailure() throws Exception {
52+
final CentralDogma client = dogma.client();
53+
final CentralDogmaRepository repo = client.forRepo("foo", "bar");
54+
final Entry<?> entry = repo.file("/a.txt").get().join();
55+
// Make sure the entry is available before testing the watcher.
56+
assertThat(entry.contentAsText().trim()).isEqualTo("Hello, world!");
57+
58+
final Watcher<String> watcher = repo.watcher(PathPattern.of("/a.txt"))
59+
.<String>map(txt -> {
60+
throw new IllegalStateException("Test exception");
61+
})
62+
.start();
63+
assertThatThrownBy(() -> {
64+
watcher.initialValueFuture().get(2, TimeUnit.SECONDS);
65+
}).isInstanceOf(ExecutionException.class)
66+
.hasCauseInstanceOf(IllegalStateException.class)
67+
.hasMessageContaining("Test exception");
68+
}
69+
}

client/java/src/main/java/com/linecorp/centraldogma/client/AbstractWatcher.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -329,11 +329,20 @@ private void doWatch(int numAttemptsSoFar) {
329329
projectName, repositoryName);
330330
logged = true;
331331
}
332-
}
332+
} else {
333+
if (cause instanceof CancellationException) {
334+
// Cancelled by close()
335+
return null;
336+
}
333337

334-
if (cause instanceof CancellationException) {
335-
// Cancelled by close()
336-
return null;
338+
if (!initialValueFuture.isDone()) {
339+
// If the initial fetch failed with non-CentralDogmaException, it is likely that some
340+
// configuration is wrong or the file is malformed. So we complete the
341+
// initialValueFuture exceptionally rather than retrying indefinitely.
342+
initialValueFuture.completeExceptionally(thrown);
343+
close();
344+
return null;
345+
}
337346
}
338347

339348
if (!logged) {

0 commit comments

Comments
 (0)