Skip to content

Commit c3ee63b

Browse files
authored
fix(discovery): discovery synchronization for stale lost targets (#689)
1 parent 25eaba2 commit c3ee63b

File tree

9 files changed

+480
-253
lines changed

9 files changed

+480
-253
lines changed

src/main/java/io/cryostat/StorageBuckets.java

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,21 @@
1515
*/
1616
package io.cryostat;
1717

18+
import java.time.Duration;
19+
import java.util.Set;
20+
import java.util.concurrent.ConcurrentHashMap;
21+
import java.util.concurrent.Executors;
22+
import java.util.concurrent.ScheduledExecutorService;
23+
import java.util.concurrent.TimeUnit;
24+
1825
import io.cryostat.util.HttpStatusCodeIdentifier;
1926

27+
import io.quarkus.runtime.ShutdownEvent;
28+
import io.quarkus.runtime.StartupEvent;
2029
import jakarta.enterprise.context.ApplicationScoped;
30+
import jakarta.enterprise.event.Observes;
2131
import jakarta.inject.Inject;
32+
import org.eclipse.microprofile.config.inject.ConfigProperty;
2233
import org.jboss.logging.Logger;
2334
import software.amazon.awssdk.services.s3.S3Client;
2435
import software.amazon.awssdk.services.s3.model.CreateBucketRequest;
@@ -30,7 +41,17 @@ public class StorageBuckets {
3041
@Inject S3Client storage;
3142
@Inject Logger logger;
3243

44+
@ConfigProperty(name = "storage.buckets.creation-retry.period")
45+
Duration creationRetryPeriod;
46+
47+
private final Set<String> buckets = ConcurrentHashMap.newKeySet();
48+
private final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();
49+
3350
public void createIfNecessary(String bucket) {
51+
buckets.add(bucket);
52+
}
53+
54+
private boolean tryCreate(String bucket) {
3455
boolean exists = false;
3556
logger.debugv("Checking if storage bucket \"{0}\" exists ...", bucket);
3657
try {
@@ -49,8 +70,27 @@ public void createIfNecessary(String bucket) {
4970
storage.createBucket(CreateBucketRequest.builder().bucket(bucket).build());
5071
logger.debugv("Storage bucket \"{0}\" created", bucket);
5172
} catch (Exception e) {
52-
logger.error(e);
73+
logger.warn(e);
74+
return false;
5375
}
5476
}
77+
return true;
78+
}
79+
80+
void onStart(@Observes StartupEvent evt) {
81+
worker.scheduleAtFixedRate(
82+
() -> {
83+
var it = buckets.iterator();
84+
while (it.hasNext()) {
85+
if (tryCreate(it.next())) it.remove();
86+
}
87+
},
88+
0,
89+
creationRetryPeriod.toMillis(),
90+
TimeUnit.MILLISECONDS);
91+
}
92+
93+
void onStop(@Observes ShutdownEvent evt) {
94+
worker.shutdown();
5595
}
5696
}

src/main/java/io/cryostat/discovery/DiscoveryNode.java

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.fasterxml.jackson.annotation.JsonView;
3232
import io.quarkus.hibernate.orm.panache.PanacheEntity;
3333
import io.quarkus.narayana.jta.QuarkusTransaction;
34+
import io.quarkus.panache.common.Parameters;
3435
import io.vertx.mutiny.core.eventbus.EventBus;
3536
import jakarta.annotation.Nullable;
3637
import jakarta.enterprise.context.ApplicationScoped;
@@ -42,6 +43,8 @@
4243
import jakarta.persistence.FetchType;
4344
import jakarta.persistence.JoinColumn;
4445
import jakarta.persistence.ManyToOne;
46+
import jakarta.persistence.NamedQueries;
47+
import jakarta.persistence.NamedQuery;
4548
import jakarta.persistence.OneToMany;
4649
import jakarta.persistence.OneToOne;
4750
import jakarta.persistence.PostPersist;
@@ -56,6 +59,11 @@
5659

5760
@Entity
5861
@EntityListeners(DiscoveryNode.Listener.class)
62+
@NamedQueries({
63+
@NamedQuery(
64+
name = "DiscoveryNode.byTypeWithName",
65+
query = "from DiscoveryNode where nodeType = :nodeType and name = :name")
66+
})
5967
public class DiscoveryNode extends PanacheEntity {
6068

6169
public static final String NODE_TYPE = "nodeType";
@@ -129,33 +137,48 @@ public static List<DiscoveryNode> findAllByNodeType(NodeType nodeType) {
129137
}
130138

131139
public static DiscoveryNode environment(String name, NodeType nodeType) {
132-
return QuarkusTransaction.joiningExisting()
133-
.call(
134-
() -> {
135-
DiscoveryNode node = new DiscoveryNode();
136-
node.name = name;
137-
node.nodeType = nodeType.getKind();
138-
node.labels = new HashMap<>();
139-
node.children = new ArrayList<>();
140-
node.target = null;
141-
node.persist();
142-
return node;
143-
});
140+
var kind = nodeType.getKind();
141+
return DiscoveryNode.<DiscoveryNode>find(
142+
"#DiscoveryNode.byTypeWithName",
143+
Parameters.with("nodeType", kind).and("name", name))
144+
.firstResultOptional()
145+
.orElseGet(
146+
() ->
147+
QuarkusTransaction.joiningExisting()
148+
.call(
149+
() -> {
150+
DiscoveryNode node = new DiscoveryNode();
151+
node.name = name;
152+
node.nodeType = kind;
153+
node.labels = new HashMap<>();
154+
node.children = new ArrayList<>();
155+
node.target = null;
156+
node.persist();
157+
return node;
158+
}));
144159
}
145160

146161
public static DiscoveryNode target(Target target, NodeType nodeType) {
147-
return QuarkusTransaction.joiningExisting()
148-
.call(
149-
() -> {
150-
DiscoveryNode node = new DiscoveryNode();
151-
node.name = target.connectUrl.toString();
152-
node.nodeType = nodeType.getKind();
153-
node.labels = new HashMap<>(target.labels);
154-
node.children = null;
155-
node.target = target;
156-
node.persist();
157-
return node;
158-
});
162+
var kind = nodeType.getKind();
163+
var connectUrl = target.connectUrl.toString();
164+
return DiscoveryNode.<DiscoveryNode>find(
165+
"#DiscoveryNode.byTypeWithName",
166+
Parameters.with("nodeType", kind).and("name", connectUrl))
167+
.firstResultOptional()
168+
.orElseGet(
169+
() ->
170+
QuarkusTransaction.joiningExisting()
171+
.call(
172+
() -> {
173+
DiscoveryNode node = new DiscoveryNode();
174+
node.name = connectUrl;
175+
node.nodeType = kind;
176+
node.labels = new HashMap<>(target.labels);
177+
node.children = null;
178+
node.target = target;
179+
node.persist();
180+
return node;
181+
}));
159182
}
160183

161184
@Override

0 commit comments

Comments
 (0)