Skip to main content

cassandra-injvm-dtest

Comprehensive guide for writing Apache Cassandra in-JVM distributed tests (dtests). Use when creating tests that simulate multi-node Cassandra clusters within a single JVM for faster integration testing. Covers cluster creation (single-node, multi-node, multi-datacenter), configuration (all cassandra.yaml parameters, features, network topology), instance lifecycle (startup/shutdown/restart), query execution, message filtering for failure scenarios, running code on instances, ClusterUtils utilities, and debugging classloader-related issues (serialization failures, same-class-different-classloader problems).

소스 정보

저장소
apache/cassandra
최근 소스 활동
2026년 6월 9일 15:31
감지된 SKILL.md 언어
영어
스타
10,118
포크
4,118

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
4 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
cassandra-injvm-dtest
version
1.0.0
description
Comprehensive guide for writing Apache Cassandra in-JVM distributed tests (dtests). Use when creating tests that simulate multi-node Cassandra clusters within a single JVM for faster integration testing. Covers cluster creation (single-node, multi-node, multi-datacenter), configuration (all cassandra.yaml parameters, features, network topology), instance lifecycle (startup/shutdown/restart), query execution, message filtering for failure scenarios, running code on instances, ClusterUtils utilities, and debugging classloader-related issues (serialization failures, same-class-different-classloader problems).
# Apache Cassandra In-JVM DTest Writer ## Overview Write comprehensive distributed tests for Apache Cassandra using the in-JVM dtest framework. This framework enables multi-node cluster simulation within a single JVM using isolated classloaders, providing fast and reliable integration testing without actual network communication. ## Core Concepts ### Framework Architecture The in-JVM dtest framework uses **classloader isolation** to run multiple Cassandra instances in one JVM: - **Shared ClassLoader**: Common classes (primitives, APIs, config objects, byte buffers) - **Per-Instance ClassLoader**: Isolated Cassandra internals (DatabaseDescriptor, singletons, all C* code) - **IsolatedExecutor**: Marshals serialized lambdas across classloader boundaries - **Message Interception**: Inter-node messaging hooked at MessagingService level (no actual networking) ### Key Classes | Class | Purpose | Location | |-------|---------|----------| | `Cluster` | Main entry point for creating clusters | `org.apache.cassandra.distributed.Cluster` | | `AbstractCluster` | Core cluster management | `org.apache.cassandra.distributed.impl.AbstractCluster` | | `Instance` | Individual Cassandra node | `org.apache.cassandra.distributed.impl.Instance` | | `InstanceConfig` | Node configuration | `org.apache.cassandra.distributed.impl.InstanceConfig` | | `ClusterUtils` | Utility methods | `org.apache.cassandra.distributed.shared.ClusterUtils` | | `ICoordinator` | Query execution interface | API package | | `MessageFilters` | Message filtering/dropping | `org.apache.cassandra.distributed.impl.MessageFilters` | ## Quick Start ### Basic Cluster Setup ```java import org.apache.cassandra.distributed.Cluster; import org.apache.cassandra.distributed.api.ConsistencyLevel; // Create and start a 3-node cluster try (Cluster cluster = Cluster.build(3).start()) { // Create keyspace cluster.schemaChange("CREATE KEYSPACE test WITH replication = " + "{'class': 'SimpleStrategy', 'replication_factor': 2}"); // Create table cluster.schemaChange("CREATE TABLE test.users (id int PRIMARY KEY, name text)"); // Execute query cluster.coordinator(1).execute("INSERT INTO test.users (id, name) VALUES (?, ?)", ConsistencyLevel.QUORUM, 1, "Alice"); // Read data Object[][] rows = cluster.coordinator(1).execute( "SELECT * FROM test.users WHERE id = ?", ConsistencyLevel.QUORUM, 1); } ``` ### Test Base Class Pattern ```java import org.apache.cassandra.distributed.test.TestBaseImpl; public class MyDTest extends TestBaseImpl { private static Cluster cluster; @BeforeClass public static void setupCluster() throws IOException { cluster = init(Cluster.build(3).start(), 2); // RF=2 } @AfterClass public static void teardownCluster() { if (cluster != null) cluster.close(); } @Test public void testSomething() { // Test implementation } } ``` ## Cluster Creation Patterns ### Pattern 1: Simple Single-Node Cluster ```java Cluster cluster = Cluster.build(1).start(); ``` ### Pattern 2: Multi-Node Cluster with Configuration ```java Cluster cluster = Cluster.build(3) .withConfig(config -> config .set("num_tokens", "256") .set("concurrent_reads", "64") .set("concurrent_writes", "64")) .start(); ``` ### Pattern 3: Multi-Datacenter Cluster ```java Cluster cluster = builder() .withRacks(2, 1, 3) // 2 DCs, 1 rack per DC, 3 nodes per rack .start(); // Or with explicit DC/rack naming Cluster cluster = builder() .withDC("dc1", 3) .withDC("dc2", 3) .start(); ``` ### Pattern 4: Cluster with Features ```java import static org.apache.cassandra.distributed.api.Feature.*; Cluster cluster = Cluster.build() .withNodes(3) .withConfig(config -> config .with(GOSSIP) // Enable gossip protocol .with(NETWORK) // Enable inter-node networking .with(NATIVE_PROTOCOL) // Enable CQL protocol .with(JMX)) // Enable JMX .start(); ``` ### Pattern 5: Custom Token Distribution ```java import org.apache.cassandra.distributed.api.TokenSupplier; Cluster cluster = Cluster.build() .withNodes(3) .withTokenSupplier(TokenSupplier.evenlyDistributedTokens(3)) .start(); ``` ### Pattern 6: Dynamic Port Allocation ```java Cluster cluster = Cluster.build(3) .withDynamicPortAllocation(true) .start(); ``` ## Configuration ### Available Configuration Parameters Configuration is applied via `config.set(key, value)` or `config.with(Feature)`: #### Network Configuration ```java config.set("broadcast_address", "127.0.0.1") .set("listen_address", "127.0.0.1") .set("rpc_address", "127.0.0.1") .set("storage_port", "7000") .set("native_transport_port", "9042") .set("jmx_port", "7199"); ``` #### Token Configuration ```java config.set("num_tokens", "256") .set("initial_token", "0"); ``` #### Memory Configuration ```java config.set("memtable_heap_space", "512MiB") .set("key_cache_size", "50MiB") .set("counter_cache_size", "50MiB") .set("row_cache_size", "0MiB"); ``` #### Compaction & Concurrency ```java config.set("concurrent_compactors", "4") .set("concurrent_reads", "32") .set("concurrent_writes", "32") .set("concurrent_counter_writes", "32"); ``` #### Commitlog Configuration ```java config.set("commitlog_sync", "periodic") .set("commitlog_sync_period_in_ms", "10000") .set("commitlog_segment_size", "32MiB"); ``` #### Authentication & Authorization ```java config.set("authenticator", "PasswordAuthenticator") .set("authorizer", "CassandraAuthorizer") .set("role_manager", "CassandraRoleManager"); ``` #### Accord Configuration (CEP-15) ```java config.set("accord.enabled", "true") .set("accord.journal_directory", "/path/to/journal"); ``` #### Data Directories ```java // Data directories are arrays config.set("data_file_directories", new String[]{"/path/to/data"}) .set("commitlog_directory", "/path/to/commitlog") .set("hints_directory", "/path/to/hints") .set("saved_caches_directory", "/path/to/saved_caches"); ``` ## Instance Lifecycle Operations ### Starting Instances ```java // Start with default settings instance.startup(); // Start with system properties ClusterUtils.start(instance, properties -> { properties.set(RING_DELAY, "5000"); properties.set(BOOTSTRAP_SCHEMA_DELAY_MS, "5000"); }); ``` ### Stopping Instances ```java // Graceful shutdown (blocking) ClusterUtils.stopUnchecked(instance); // Abrupt shutdown (simulates kill -9) ClusterUtils.stopAbrupt(cluster, instance); // Stop all instances ClusterUtils.stopAll(cluster); // Async shutdown Future<?> shutdownFuture = instance.shutdown(); shutdownFuture.get(); // wait for completion ``` ### Restarting Instances ```java // Simple restart ClusterUtils.restartUnchecked(instance); // Restart with different configuration instance.shutdown().get(); instance.config().set("concurrent_reads", "64"); instance.startup(); ``` ### Adding Instances ```java // Add instance with same config as existing nodes IInstance newInstance = ClusterUtils.addInstance(cluster); // Add instance with custom config IInstance newInstance = ClusterUtils.addInstance(cluster, config -> { config.set("auto_bootstrap", true); }); // Add instance to specific DC/rack IInstance newInstance = ClusterUtils.addInstance(cluster, "dc1", "rack1", config -> { config.set("num_tokens", "256"); }); // Start the new instance newInstance.startup(); ``` ### Replacing Instances ```java // Replace a failed node IInstance replacement = ClusterUtils.replaceHostAndStart(cluster, failedInstance); // Replace with custom properties IInstance replacement = ClusterUtils.replaceHostAndStart( cluster, failedInstance, (inst, properties) -> { properties.set(RING_DELAY, "5000"); } ); ``` ## Running Code on Instances ### Pattern 1: Run Without Return Value ```java cluster.get(1).runOnInstance(() -> { StorageService.instance.forceKeyspaceFlush("system"); }); ``` ### Pattern 2: Get Return Value (Sync) ```java String version = cluster.get(1).callOnInstance(() -> { return FBUtilities.getReleaseVersionString(); }); ``` ### Pattern 3: Get Return Value (Async) ```java Future<String> future = cluster.get(1).asyncCallOnInstance(() -> { return FBUtilities.getReleaseVersionString(); }); String version = future.get(); ``` ### Pattern 4: Pass Parameters ```java // Parameters must be serializable int result = cluster.get(1).callOnInstance(() -> { // Access Cassandra internals here return ClusterMetadata.current().directory.allAddresses().size(); }); ``` ### Pattern 5: Instance-to-Instance Operations ```java // Get node ID from one instance, use it on another int nodeId = ClusterUtils.getNodeId(cluster.get(1), cluster.get(2)); NodeId id = new NodeId(nodeId); cluster.get(2).runOnInstance(() -> { StorageService.instance.cancelInProgressSequences(id); }); ``` ## Query Execution ### Basic Queries ```java // Execute without results cluster.coordinator(1).execute( "INSERT INTO test.users (id, name) VALUES (?, ?)", ConsistencyLevel.QUORUM, 1, "Alice" ); // Execute with results Object[][] rows = cluster.coordinator(1).execute( "SELECT * FROM test.users WHERE id = ?", ConsistencyLevel.QUORUM, 1 ); ``` ### Using Query Results ```java import org.apache.cassandra.distributed.api.SimpleQueryResult; SimpleQueryResult result = cluster.coordinator(1).executeWithResult( "SELECT * FROM test.users", ConsistencyLevel.QUORUM ); while (result.hasNext()) { Row row = result.next(); int id = row.getInteger("id"); String name = row.getString("name"); System.out.println("User: " + id + ", " + name); } ``` ### Pagination ```java SimpleQueryResult result = cluster.coordinator(1).executeWithPagingWithResult( "SELECT * FROM test.users", ConsistencyLevel.QUORUM, 100 // page size ); ``` ### Async Execution ```java Future<SimpleQueryResult> future = cluster.coordinator(1).asyncExecuteWithResult( "SELECT * FROM test.users",
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기