← Ferrosa Suite home · Database home

CQL Reference

Ferrosa implements the CQL native protocol with negotiation capped at v4, including tested driver paths such as cdrs-tokio. This page documents what's supported, what's different, and what Ferrosa adds on top.

Beta: Ferrosa is currently in beta. CQL compatibility is under active development.

On this page

Protocol

Wire protocol

Ferrosa speaks the CQL native protocol with the standard 9-byte frame header and caps negotiation at protocol v4. v5 added a modern framing layer that drivers implement inconsistently — some send plain legacy envelopes at v5, others send CRC-checksummed modern frames — so no single server mode serves both. A v5 STARTUP is therefore rejected with a protocol-version error advertising v4 as the maximum, and every compliant driver (gocql, DataStax Java/C#, scylla-rust, cassandra-driver) transparently falls back to the one well-tested v4 transport, including cdrs-tokio. All 16 opcodes are handled:

OpcodeNameDirection
0x00ERRORResponse
0x01STARTUPRequest
0x02READYResponse
0x03AUTHENTICATEResponse
0x05OPTIONSRequest
0x06SUPPORTEDResponse
0x07QUERYRequest
0x08RESULTResponse
0x09PREPARERequest
0x0AEXECUTERequest
0x0BREGISTERRequest
0x0CEVENTResponse
0x0DBATCHRequest
0x0EAUTH_CHALLENGEResponse
0x0FAUTH_RESPONSERequest
0x10AUTH_SUCCESSResponse

Compression

Frame-level compression is negotiated during STARTUP via the COMPRESSION option:

Compression is optional. Uncompressed frames are always accepted.

Authentication

SASL PLAIN authentication via the standard org.apache.cassandra.auth.PasswordAuthenticator flow. Passwords are hashed with bcrypt or argon2. Disable with FERROSA_AUTH_DISABLED=true for development.

Driver bootstrap and topology discovery

Driver bootstrap follows the standard Cassandra-compatible sequence. There is no Ferrosa-specific handshake required to unlock topology metadata:

  1. Connect to an initial contact point.
  2. Optionally exchange OPTIONS / SUPPORTED.
  3. Send STARTUP.
  4. If auth is enabled, complete AUTHENTICATEAUTH_RESPONSEAUTH_SUCCESS. Otherwise the server returns READY directly.
  5. Only after the connection reaches READY or AUTH_SUCCESS does the driver query system.local, system.peers, and system.peers_v2.
  6. The driver uses those system-table rows to discover tokens, schema version, and the peer endpoints it should dial next.

This means post-auth hangs that occur while a driver is opening peer connections usually indicate bad topology metadata, not a missing bootstrap message. Ferrosa must advertise peer addresses that are reachable from the client's network.

Explicitly: OPTIONS does not control whether the client receives public or internal peer addresses. Neither do STARTUP, compression settings, or any Ferrosa-specific extension flag. The server chooses which addresses to return based on the source IP of the established client connection when servicing the topology queries.

Data Types

CQL TypeProtocol IDStatus
ascii0x0001Supported
bigint0x0002Supported
blob0x0003Supported
boolean0x0004Supported
counter0x0005Supported
decimal0x0006Supported
double0x0007Supported
float0x0008Supported
int0x0009Supported
timestamp0x000BSupported
uuid0x000CSupported
varchar / text0x000DSupported
varint0x000ESupported
timeuuid0x000FSupported
inet0x0010Supported
date0x0011Supported
time0x0012Supported
smallint0x0013Supported
tinyint0x0014Supported
list<T>0x0020Supported
map<K, V>0x0021Supported
set<T>0x0022Supported
tuple<...>0x0031Supported
frozen<T>Supported
duration0x0015Supported
UDT0x0030Supported
vector<float, N>0x0033Supported

Collection bind values in prepared statements are fully supported. Map, set, and list values bound via EXECUTE are stored in Cassandra-compatible wire format and survive flush, compaction, and S3 upload without format loss.

DDL Statements

StatementStatusNotes
CREATE KEYSPACESupportedWITH replication (SimpleStrategy / NetworkTopologyStrategy), IF NOT EXISTS, DURABLE_WRITES. Transient replication ('DC1': '3/1') is rejected — Ferrosa does not implement it, and accepting it would corrupt driver schema metadata.
ALTER KEYSPACESupportedChange replication, durable_writes
DROP KEYSPACESupportedIF EXISTS
CREATE TABLESupportedPartition key, clustering key with order, IF NOT EXISTS, table options (compaction, compression, comment, TTL, gc_grace)
ALTER TABLESupportedADD column, DROP column
DROP TABLESupportedIF EXISTS
CREATE INDEXSupportedUSING type, WITH OPTIONS, IF NOT EXISTS — see below
DROP INDEXSupportedIF EXISTS
DROP (without TABLE keyword)SupportedDROP keyspace_name.table_name shorthand
CREATE MATERIALIZED VIEWNot yet
CREATE TYPE (UDT)SupportedCREATE TYPE, ALTER TYPE, DROP TYPE — full UDT DDL lifecycle
CREATE FUNCTION / AGGREGATESupportedCREATE/DROP FUNCTION, CREATE/DROP AGGREGATE — full DDL lifecycle with cluster replication. UDFs are WebAssembly components (LANGUAGE wasm AS '<hex>' | AS FILE '…' | AS URL '…' WITH SHA256 = '…'); Java UDFs are not supported. Keyspace-qualified calls (ks.fn(args)) work in SELECT.

Prepared statements remain valid after ALTER TABLE ADD or DROP column — the schema snapshot updates atomically and subsequent PREPARE calls reflect the current column set.

Example

CREATE KEYSPACE social WITH replication = {
  'class': 'SimpleStrategy',
  'replication_factor': 3
} AND durable_writes = true;

CREATE TABLE social.users (
  user_id uuid,
  name text,
  email text,
  created_at timestamp,
  tags set<text>,
  PRIMARY KEY (user_id)
);

-- Per-table compaction strategy (default: STCS)
CREATE TABLE social.events (
  event_id uuid,
  payload text,
  PRIMARY KEY (event_id)
) WITH compaction = {
  'class': 'UnifiedCompactionStrategy',
  'fan_factor': '4'
};

Compaction Strategies

StrategyDDL Class NameBehavior
STCS (default)none requiredSize-Tiered — groups SSTables by similar size
UCSUnifiedCompactionStrategyDensity-based levels with fan factor: W=2 (LCS-like), W=4 (balanced), W=32 (STCS-like)

DML Statements

StatementStatusNotes
SELECTSupportedWHERE, ORDER BY, LIMIT, DISTINCT, bind markers (?), named bind markers (:name), IN clause, ALLOW FILTERING, token(), CONTAINS / CONTAINS KEY, LIKE, SOUNDS LIKE, fts_match()
INSERTSupportedIF NOT EXISTS (LWT), USING TTL, USING TIMESTAMP
UPDATESupportedSET, WHERE, IF conditions (LWT), collection +/- operators, counter increment/decrement
DELETESupportedWHERE, IF EXISTS / IF conditions (LWT), column-level delete, map element delete syntax
BATCHSupportedBEGIN BATCH ... APPLY BATCH (unlogged), batch CAS (conditional batch)
BEGIN TRANSACTIONSupportedBEGIN TRANSACTION ... COMMIT TRANSACTION / ROLLBACK TRANSACTION (Accord)
TRUNCATESupported
USESupportedSet default keyspace for session

Built-in functions

FunctionStatusNotes
toJson()SupportedSerialize any column to JSON string
token()SupportedToken-range queries in WHERE clauses
uuid()SupportedGenerate random UUID
now()SupportedCurrent timeuuid
toTimestamp()SupportedConvert timeuuid to timestamp
currentDate()SupportedToday's date (for temporal arithmetic in WHERE)

Durations & temporal arithmetic

The duration type accepts both literal forms — compact (2d, 1mo3d, 89h4m48s) and ISO-8601 (P1Y2M3D, PT4H5M6S). A WHERE predicate may add or subtract a duration from a date or timestamp; months and days are applied calendar-aware (e.g. Jan 31 + 1mo clamps to the last day of February).

-- rows from the last two days
SELECT * FROM events
  WHERE day >= currentDate() - 2d ALLOW FILTERING;

Prepared statements

Full PREPARE/EXECUTE support with positional bind values. PREPARE responses include pk_count metadata for driver routing. The prepared statement cache uses moka with W-TinyLFU eviction (64 MB default). Bind markers (?) and named bind markers (:name) are both supported.

Bind values are fully supported in QUERY frames — positional (?) and named (:name) markers work in all statement types: SELECT, INSERT, UPDATE, DELETE. Collection-typed bind values (map, set, list) are passed through in Cassandra wire format for correct round-trip storage.

-- Prepare
PREPARE stmt FROM 'SELECT * FROM social.users WHERE user_id = ?';

-- Execute with positional bind
EXECUTE stmt USING 550e8400-e29b-41d4-a716-446655440000;

Comparison operators

WHERE clauses support: =, <, >, <=, >=, !=, IN, CONTAINS, CONTAINS KEY, LIKE

LIKE matches text patterns with the % wildcard (any sequence of characters) — 'pre%' (prefix), '%suf' (suffix), '%mid%' (contains), or an exact literal. Matching is case-sensitive. As a non-key predicate it is evaluated as a post-scan filter and requires ALLOW FILTERING.

-- names beginning with 'M'
SELECT * FROM cycling.cyclist_name
  WHERE firstname LIKE 'M%' ALLOW FILTERING;

Consistency levels

Ferrosa supports the following consistency levels for reads and writes:

Set per-query via the standard CQL protocol flags, or configure a default with FERROSA_DEFAULT_CL (default: QUORUM).

Conflict resolution

Cells reconcile by last-write-wins on timestamp: the higher write timestamp wins. When two writes to the same cell share the same timestamp, the lexicographically greater value wins, so the result is deterministic regardless of replica or compaction order.

Difference from Cassandra: when a write and a delete carry the same timestamp, Ferrosa favors the write — the data is preserved. Apache Cassandra resolves the same tie in favor of the delete (the tombstone wins). If you depend on Cassandra's delete-wins-on-tie behavior, use distinct timestamps for the delete.

Auth & Roles

StatementStatusNotes
CREATE ROLESupportedWITH PASSWORD, HASHED PASSWORD, SUPERUSER, LOGIN, OPTIONS, IF NOT EXISTS
ALTER ROLESupportedChange PASSWORD / HASHED PASSWORD, SUPERUSER, LOGIN
CREATE USER / ALTER USERSupportedLegacy alias; WITH [HASHED] PASSWORD, SUPERUSER / NOSUPERUSER
DROP ROLESupportedIF EXISTS
GRANTSupportedGRANT perm[, perm…] ON resource TO role. Resource: ON TABLE ks.t / ks.t, ON KEYSPACE, ON ALL KEYSPACES, ON ALL ROLES, ON FUNCTION. Unknown permissions are rejected (no silent partial grant).
REVOKESupportedREVOKE perm[, perm…] ON resource FROM role (same resource forms as GRANT)
GRANT / REVOKE roleSupportedGRANT <role> TO <member> / REVOKE <role> FROM <member> — role hierarchy (the member inherits the role's permissions). Replicated additively (one membership edge per op) and cycle-checked at apply, so concurrent grants never clobber and a revoke is never lost to a racing grant.
LIST ROLESSupportedLIST ROLES [OF <role>] [NORECURSIVE]; LIST USERS alias
LIST PERMISSIONSSupportedLIST [ALL | <permission>] PERMISSIONS [ON <resource>] [OF <role>] [NORECURSIVE] — gated on DESCRIBE over all roles
ACCESS TO/FROM (network auth)RejectedACCESS TO DATACENTERS / FROM CIDRS is parsed but rejected — ferrosa has no network authorizer, so an unenforced access restriction fails loud rather than being silently accepted

HASHED PASSWORD. CREATE ROLE … WITH HASHED PASSWORD = '<hash>' (and the legacy CREATE USER … WITH HASHED PASSWORD '<hash>') stores a pre-computed bcrypt or argon2id hash verbatim — it is not re-hashed, which lets you migrate existing credentials. The hash format is validated on the coordinator and rejected if unsupported, so a role can never hold a credential the authenticator cannot verify. Password and hash literals are redacted from any log line or error message.

-- Plaintext password — hashed server-side with bcrypt / argon2id
CREATE ROLE app_writer WITH PASSWORD = 'S3cret-Pass!' AND LOGIN = true;

-- Migrate an existing credential: a pre-computed hash, stored verbatim
CREATE ROLE legacy_user
  WITH HASHED PASSWORD = '$2a$10$JSJEMFm6GeaW9XxT5JIheuEtPvat6i7uKbnTcxX3c1wshIIsGyUtG'
  AND LOGIN = true;

-- Custom authenticator OPTIONS (accepted; not interpreted by the default authenticator)
CREATE ROLE svc WITH PASSWORD = 'p' AND OPTIONS = { 'ttl' : '3600' };

-- Rotate to a new hashed credential, or toggle superuser/login
ALTER ROLE app_writer WITH HASHED PASSWORD = '$argon2id$v=19$m=19456,t=2,p=1$c29tZXNhbHQ$aGFzaA';

-- Legacy USER alias: no '=', trailing SUPERUSER / NOSUPERUSER
CREATE USER ops WITH HASHED PASSWORD '$2a$10$JSJEMFm6GeaW9XxT5JIheuEtPvat6i7uKbnTcxX3c1wshIIsGyUtG' NOSUPERUSER;

-- Network authorization is parsed but REJECTED (no network authorizer):
--   CREATE ROLE r WITH PASSWORD = 'p' AND ACCESS TO DATACENTERS {'DC1'};  -- error

Ferrosa supports column-level permissions, rate limiting per role, and audit logging to configurable sinks (log file, audit table).

System Keyspaces

system_schema

Standard Cassandra system schema tables for driver compatibility:

system.local

Node metadata including tokens column for driver topology awareness. Drivers typically read this immediately after READY or AUTH_SUCCESS as part of topology discovery.

system_observability (Ferrosa extension)

Virtual tables backed by live in-memory state, not persisted:

-- Check active connections
SELECT * FROM system_observability.connections;

-- Monitor running queries
SELECT * FROM system_observability.active_queries;

-- Storage engine stats
SELECT * FROM system_observability.storage_stats;

Secondary Indexes

Beyond Cassandra: Ferrosa's secondary index framework goes far beyond Cassandra's SAI or legacy 2i indexes. Eight pluggable index types cover traditional queries, full-text phonetic search, and AI/ML vector similarity — all built into the database without requiring a separate search service for the preview path.

Index types

TypeUSING clauseUse case
B-tree'btree' (default)Ordered range queries and sorted scans
Hash'hash'O(1) equality point lookups
Composite'composite'Multi-column prefix-based lookups
Phonetic'phonetic'Fuzzy name matching (Soundex, Metaphone, Double Metaphone, Caverphone)
FilteredAny + WHEREPartial index over a subset of rows
Vector (HNSW)'vector'Approximate nearest neighbor — graph-based, best query performance
Vector (IVFFlat)'vector'Approximate nearest neighbor — k-means clustering, faster builds
Vector (HVQ)'vector' WITH OPTIONS = {'method':'hvq'}Quantized approximate nearest neighbor — reads far fewer bytes per query (evaluation)

Creating indexes

-- B-tree index (default when USING is omitted)
CREATE INDEX idx_email ON users (email) USING 'btree';

-- Hash index for fast equality lookups
CREATE INDEX idx_user_id ON sessions (user_id) USING 'hash';

-- Composite index across multiple columns
CREATE INDEX idx_name ON users (last_name, first_name) USING 'composite';

-- Phonetic index for "sounds like" matching
CREATE INDEX idx_name_phonetic ON users (last_name)
  USING 'phonetic' WITH OPTIONS = {'algorithm': 'double_metaphone'};

-- Filtered index — only index active users
CREATE INDEX idx_active_email ON users (email)
  USING 'btree' WHERE status = 'active';

-- IF NOT EXISTS is supported
CREATE INDEX IF NOT EXISTS idx_email ON users (email);

-- Drop an index
DROP INDEX idx_email;
DROP INDEX IF EXISTS idx_email;

Vector indexes for AI and similarity search

Ferrosa includes two vector index algorithms for approximate nearest neighbor (ANN) search, supporting AI embeddings, semantic search, and recommendation systems:

-- HNSW index — best query performance, incremental builds
CREATE INDEX idx_embed ON documents (embedding)
  USING 'vector' WITH OPTIONS = {
    'method': 'hnsw',
    'metric': 'cosine',
    'dimensions': '768',
    'm': '16',
    'ef_construction': '200'
  };

-- IVFFlat index — faster builds, good for batch imports
CREATE INDEX idx_embed_ivf ON documents (embedding)
  USING 'vector' WITH OPTIONS = {
    'method': 'ivfflat',
    'metric': 'l2',
    'dimensions': '1536',
    'lists': '100'
  };

Distance metrics

MetricValueUse case
L2 (Euclidean)'l2'Standard distance — smaller = more similar
Cosine'cosine'Angle-based similarity — ideal for text embeddings
Inner product'inner_product'Dot product — larger = more similar

Supports up to 4,096 dimensions (f32) or 8,192 dimensions (f16 half-precision).

Full-text search indexes

Ferrosa includes built-in full-text search with inverted index sidecars and BM25 ranked retrieval. No external search engine required.

-- Create a full-text index on a text column
CREATE INDEX idx_body ON articles (body)
  USING 'fulltext';

-- Query with fts_match() — boolean operators, phrase, prefix
SELECT * FROM articles
  WHERE body = fts_match('distributed AND database');

SELECT * FROM articles
  WHERE body = fts_match('"S3 backed storage"');

SELECT * FROM articles
  WHERE body = fts_match('compac*');

SELECT * FROM articles
  WHERE body = fts_match('NOT deprecated');
Query syntaxExampleDescription
Single term'rust'Match documents containing the analyzed term
AND'rust AND database'Both terms must be present
OR'rust OR go'Either term must be present
NOT'NOT deprecated'Exclude documents matching the term
Phrase'"exact phrase"'All words must appear (proximity approximation)
Prefix'compac*'Wildcard prefix expansion (capped at 10K terms)

Results are ranked by BM25 relevance score. The default analyzer lowercases, removes English stop words, and applies Porter stemming. Custom stop words can be configured via index options.

NVMe table pinning

Pin hot tables to local NVMe storage for latency-sensitive reads, avoiding object-store fetches on the hot path:

-- Create a pinned table
CREATE TABLE session_cache (
  session_id uuid PRIMARY KEY,
  user_id uuid,
  data blob
) WITH extensions = {'storage.pin': 'nvme'};

-- Pin with size cap (evict oldest beyond 10 GB)
CREATE TABLE hot_lookups (
  key text PRIMARY KEY,
  value blob
) WITH extensions = {'storage.pin': 'nvme',
  'storage.pin_max_bytes': '10737418240'};

-- Toggle pin on a live table
ALTER TABLE session_cache
  WITH extensions = {'storage.pin': 'none'};

Pinned tables trade durability for latency — data is lost on node replacement unless replicated. Commit log still provides crash recovery within a single node.

Phonetic algorithms

AlgorithmValueBest for
Soundex'soundex'Standard American English names
Metaphone'metaphone'General English pronunciation
Double Metaphone'double_metaphone'Multi-origin names (returns primary + alternate codes)
Caverphone'caverphone'New Zealand English names

How indexes work

Ferrosa indexes are storage-attached — built as companion files alongside SSTables. Indexes are built asynchronously after memtable flush, off the foreground write acknowledgement path. This means:

Monitoring index status

-- Check index build status and staleness
SELECT index_name, status, pending_sstable_count, lag_seconds
FROM system_views.secondary_indexes;

-- View index metadata
SELECT index_name, kind, target, options
FROM system_schema.indexes
WHERE keyspace_name = 'myapp';

Multi-node replication

All index DDL (CREATE INDEX, DROP INDEX) replicates automatically in pair mode. Each node independently builds indexes for its local SSTables — no cross-node index coordination needed.

Transactions (Accord)

Strict Serializable: Ferrosa implements distributed transactions via the Accord consensus protocol — providing strict serializability without Paxos. All Cassandra lightweight transaction (LWT) patterns are supported, plus multi-statement transactions.

Lightweight transactions (LWT)

Supported conditional-mutation patterns for lightweight transactions:

PatternStatusNotes
INSERT ... IF NOT EXISTSSupportedReturns [applied] boolean column
UPDATE ... IF conditionSupportedCompare-and-set on any column; supports =, !=, <, >, <=, >=, IN
DELETE ... IF EXISTSSupportedConditional delete
DELETE ... IF conditionSupportedConditional delete with column checks
Batch CASSupportedBEGIN BATCH with IF conditions across statements
-- Insert only if the row doesn't exist
INSERT INTO accounts (id, balance, owner)
VALUES ('acct-1', 1000, 'Alice')
IF NOT EXISTS;

-- Conditional update (compare-and-set)
UPDATE accounts SET balance = 900
WHERE id = 'acct-1'
IF balance = 1000;

-- Conditional delete
DELETE FROM accounts
WHERE id = 'acct-1'
IF balance = 0;

Multi-statement transactions

Ferrosa extends CQL with explicit transaction blocks for multi-partition atomic operations:

-- Atomic transfer across partitions
BEGIN TRANSACTION
  UPDATE accounts SET balance = balance - 100
    WHERE id = 'acct-1' IF balance >= 100;
  UPDATE accounts SET balance = balance + 100
    WHERE id = 'acct-2';
COMMIT TRANSACTION;

-- Rollback on failure
ROLLBACK TRANSACTION;

Consistency levels for transactions

LWT operations use SERIAL or LOCAL_SERIAL consistency for the read phase, combined with any standard write consistency level. This matches Cassandra's LWT behavior exactly.

How it works

Ferrosa uses the Accord consensus protocol (not Paxos) for transaction coordination:

SUBSCRIBE Extension

Developer-preview note: SUBSCRIBE is a Ferrosa-specific CQL extension under active verification. Table-level polling/subscription examples are design targets; arbitrary SELECT streaming and driver behavior are not yet public compatibility guarantees.

Syntax

-- Subscribe to a table with polling interval
SUBSCRIBE keyspace.table EVERY 5s;

-- Subscribe with push-on-write (change data capture)
SUBSCRIBE keyspace.table DELTA;

-- Arbitrary SELECT and observability subscriptions remain verification work

-- Unsubscribe from a specific stream
UNSUBSCRIBE 42;

-- Unsubscribe from all streams
UNSUBSCRIBE;

Modes

ModeSyntaxBehavior
EVERYEVERY 5sServer re-executes the query at the given interval and pushes results
DELTADELTAPush-on-write via SubscriptionObserver in the commit log; only changed rows are sent

Backward compatibility

SUBSCRIBE response framing is still being verified against real drivers. Treat this extension as experimental until compatibility evidence is attached.

Graph (Cypher) Extension

Ferrosa Extension: Cypher graph queries run against CQL tables annotated with graph schema extensions. Queries are sent to a separate HTTP/JSON endpoint on port 7474.

Schema extensions

-- Mark a table as a vertex type
ALTER TABLE social.users
  WITH extensions = {'graph.type': 'vertex', 'graph.label': 'Person'};

-- Mark a table as an edge type
ALTER TABLE social.follows
  WITH extensions = {
    'graph.type': 'edge',
    'graph.label': 'FOLLOWS',
    'graph.source': 'Person',
    'graph.target': 'Person'
  };

Supported Cypher statements

StatementStatusNotes
MATCH ... RETURNSupportedPattern matching, WHERE clause, multi-hop
CREATEParsedParsed; mutate via CQL INSERT (see Cypher reference)
SETParsedParsed; mutate via CQL UPDATE
DELETE / DETACH DELETEParsedParsed; mutate via CQL DELETE
SUBSCRIBE MATCHExperimentalGraph streaming syntax is documented as a developer-preview target pending verification

Graph SUBSCRIBE

Graph SUBSCRIBE examples are retained as experimental syntax, not a public production guarantee.

-- Experimental graph traversal subscription
SUBSCRIBE MATCH (a:Person {name: 'Alice'})-[:FOLLOWS]->(b:Person)
RETURN b.name, b.email
EVERY 10s;

-- Push-on-write for graph changes
SUBSCRIBE MATCH (a:Person)-[r:FOLLOWS]->(b:Person)
RETURN a.name, b.name
DELTA;

Not Yet Supported

The following Cassandra features are not yet implemented. Applications that use them require code changes before migrating:

FeatureStatusImpact
GROUP BYNot supportedSELECT statements with GROUP BY are rejected at parse time; queries must be restructured or aggregation moved to the application layer.
PER PARTITION LIMITNot supportedThe PER PARTITION LIMIT n clause is not parsed; rewrite as application-side limiting or use clustering key range predicates.
Query tracingNot supportedThe TRACING flag in QUERY/EXECUTE frames is accepted but silently ignored — no system_traces rows are written. Tools that depend on tracing data (DevCenter, some observability agents) will see empty trace results.
Schema-change EVENT pushWired but inertREGISTER is accepted and READY is returned, but Ferrosa never sends EVENT frames to registered clients. Drivers that rely on server-push schema invalidation (some DataStax driver versions with schema_event_refresh_delay disabled) must be configured to poll instead, or schema changes will not be reflected until the connection is recycled.
Materialized viewsNot supportedCREATE MATERIALIZED VIEW is rejected; queries that read from materialized views must be rewritten against base tables.
Java / JavaScript UDFsNot supportedCassandra CREATE FUNCTION ... LANGUAGE java and LANGUAGE javascript are rejected. Ferrosa supports LANGUAGE wasm and LANGUAGE assemblyscript only; existing Java UDFs must be recompiled to WebAssembly.

Behavioral Differences

These features exist in both Cassandra and Ferrosa but behave differently. Applications that depend on the Cassandra behavior may produce incorrect results without code changes:

AreaCassandra behaviorFerrosa behaviorImpact
Write-vs-delete tie on same timestamp Tombstone wins (delete beats write) Write wins (data is preserved) Applications that delete and re-insert with the same client timestamp may observe stale data. Use distinct timestamps for deletes.
LOGGED batch crash recovery Uses a batchlog table on a coordinator peer for replay on coordinator failure Single-node: atomic group commit in the commit log (no batchlog). Cluster: full 3-phase batchlog protocol. No application behavior difference in the non-crash path. Single-node Ferrosa provides equivalent crash recovery via the commit log without a separate batchlog table.
Secondary index consistency window SAI indexes are updated synchronously on the write path Indexes are built asynchronously after memtable flush; a brief staleness window exists between write acknowledgement and index availability Queries immediately after a write may not see the new row via an index. Use partition-key reads for read-your-writes guarantees. Monitor staleness via system_views.secondary_indexes.
Counter multi-node reconciliation Distributed sharded counters with per-replica local increments and reconciliation Counter increment/decrement is executed via the standard write path; full distributed counter reconciliation semantics are not independently verified Counter values may diverge under concurrent multi-node increments. Validate counter workloads before production migration.
Driver compatibility: Ferrosa's core CQL paths — DDL, DML, prepared statements, unlogged and logged batches, LWT, system keyspaces, and SASL authentication — are exercised by automated driver smoke tests on every CI build across six language drivers: Python (cassandra-driver), Go (gocql), Node.js (cassandra-driver), Java (DataStax Java Driver), C# (DataStax C# Driver), and Rust (scylla-rust-driver). Applications that limit themselves to these paths connect and run without driver changes. See .github/workflows/driver-tests.yml and tests/drivers/ for the test matrix.