Skip to content

Commit 7ac3ac8

Browse files
authored
CASSSIDECAR-245: Implementation of CassandraClusterSchema (#251)
Patch by Bernardo Botella; Reviewed by Francisco Guerrero for CASSSIDECAR-245
1 parent 253b726 commit 7ac3ac8

24 files changed

Lines changed: 3156 additions & 9 deletions

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
0.3.0
22
-----
3+
* Implementation of CassandraClusterSchemaMonitor (CASSSIDECAR-245)
34
* Sidecar endpoint for vending statistics related to compaction (CASSSIDECAR-329)
45
* Update logging dependencies (CASSSIDECAR-337)
56

conf/sidecar.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ sidecar:
107107
maximum_size: 10000
108108
cdc:
109109
segment_hardlink_cache_expiry: 5m # 5 minutes
110+
table_schema_refresh_time: 60s # Interval time between table schema refresh for CDC
110111
worker_pools:
111112
service:
112113
name: "sidecar-worker-pool"

gradle.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,6 @@ smallryeOpenApiVersion=4.1.1
4040
microprofileOpenApiVersion=3.1.1
4141
jakartaWsRsVersion=3.1.0
4242
swaggerVersion=2.2.21
43+
# Cdc dependencies
44+
kryoVersion=4.0.2
45+
analyticsVersion=0.1.0

server/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ dependencies {
143143
implementation("org.eclipse.microprofile.openapi:microprofile-openapi-api:${project.microprofileOpenApiVersion}")
144144
implementation("jakarta.ws.rs:jakarta.ws.rs-api:${project.jakartaWsRsVersion}")
145145

146+
implementation(group: "org.apache.cassandra", name: "cassandra-analytics-common", version: "${[project.analyticsVersion]}")
147+
implementation(group: "org.apache.cassandra", name: "cassandra-analytics-cdc_spark3_2.12", version: "${[project.analyticsVersion]}")
148+
implementation "com.esotericsoftware:kryo-shaded:${kryoVersion}"
149+
146150
testImplementation "org.junit.jupiter:junit-jupiter-api:${project.junitVersion}"
147151
testImplementation "org.junit.jupiter:junit-jupiter-params:${project.junitVersion}"
148152
testImplementation "org.assertj:assertj-core:3.24.2"
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.cassandra.bridge;
21+
22+
import java.lang.reflect.Constructor;
23+
import java.lang.reflect.InvocationTargetException;
24+
import java.net.MalformedURLException;
25+
import java.net.URL;
26+
import java.security.AccessController;
27+
import java.security.PrivilegedAction;
28+
import java.util.Arrays;
29+
import java.util.Map;
30+
import java.util.Objects;
31+
import java.util.concurrent.ConcurrentHashMap;
32+
33+
34+
import jakarta.inject.Singleton;
35+
import org.jetbrains.annotations.NotNull;
36+
37+
import static org.apache.cassandra.bridge.BaseCassandraBridgeFactory.getCassandraVersion;
38+
39+
/**
40+
* Factory class for creating Cassandra bridge instances based on version-specific jar files.
41+
* <p>
42+
* This factory maintains a cache of CassandraBridge instances mapped by Cassandra version labels
43+
* and provides methods to retrieve bridge instances for specific Cassandra versions.
44+
* Each bridge is loaded from version-specific JAR resources and instantiated using reflection.
45+
*/
46+
@Singleton
47+
public class CassandraBridgeFactory
48+
{
49+
// maps Cassandra version-specific jar name (e.g. 'four-zero') to matching CassandraBridge
50+
private final Map<String, CassandraBridge> cassandraBridges;
51+
52+
public CassandraBridgeFactory()
53+
{
54+
cassandraBridges = new ConcurrentHashMap<>(CassandraVersion.values().length);
55+
}
56+
57+
@NotNull
58+
public CassandraBridge get(@NotNull String version)
59+
{
60+
return get(getCassandraVersion(version));
61+
}
62+
63+
@NotNull
64+
public CassandraBridge get(@NotNull CassandraVersionFeatures features)
65+
{
66+
return get(getCassandraVersion(features));
67+
}
68+
69+
@NotNull
70+
public CassandraBridge get(@NotNull CassandraVersion version)
71+
{
72+
String jarBaseName = version.jarBaseName();
73+
Objects.requireNonNull(jarBaseName, "Cassandra version " + version + " is not supported");
74+
return cassandraBridges.computeIfAbsent(jarBaseName, this::create);
75+
}
76+
77+
@NotNull
78+
@SuppressWarnings("unchecked")
79+
private CassandraBridge create(@NotNull String label)
80+
{
81+
try
82+
{
83+
ClassLoader loader = buildClassLoader(
84+
cassandraResourceName(label),
85+
bridgeResourceName(label),
86+
typesResourceName(label));
87+
Class<CassandraBridge> bridge = (Class<CassandraBridge>) loader.loadClass(CassandraBridge.IMPLEMENTATION_FQCN);
88+
Constructor<CassandraBridge> constructor = bridge.getConstructor();
89+
return constructor.newInstance();
90+
}
91+
catch (ClassNotFoundException | NoSuchMethodException | InstantiationException
92+
| IllegalAccessException | InvocationTargetException exception)
93+
{
94+
throw new RuntimeException("Failed to create Cassandra bridge for label " + label, exception);
95+
}
96+
}
97+
98+
@NotNull
99+
String cassandraResourceName(@NotNull String label)
100+
{
101+
return "/bridges/" + label + ".jar";
102+
}
103+
104+
@NotNull
105+
String bridgeResourceName(@NotNull String label)
106+
{
107+
return jarResourceName(label, "bridge");
108+
}
109+
110+
@NotNull
111+
String typesResourceName(@NotNull String label)
112+
{
113+
return jarResourceName(label, "types");
114+
}
115+
116+
String jarResourceName(String... parts)
117+
{
118+
return "/bridges/" + String.join("-", parts) + ".jar";
119+
}
120+
121+
public ClassLoader buildClassLoader(String... resourceNames)
122+
{
123+
URL[] urls = Arrays.stream(resourceNames)
124+
.map(BaseCassandraBridgeFactory::copyClassResourceToFile)
125+
.map(jar -> {
126+
try
127+
{
128+
return jar.toURI().toURL();
129+
}
130+
catch (MalformedURLException e)
131+
{
132+
throw new RuntimeException(e);
133+
}
134+
}).toArray(URL[]::new);
135+
136+
return AccessController.doPrivileged((PrivilegedAction<ClassLoader>) () ->
137+
new PostDelegationClassLoader(urls, Thread.currentThread().getContextClassLoader()));
138+
}
139+
140+
}

0 commit comments

Comments
 (0)