- Sources
- Database
Apache Cassandra
Overview
Section titled “Overview”Signals reads event and customer data straight from a Cassandra cluster on a schedule and sends the columns you nominate to your ad platforms.
Reading it directly means there is no file job to maintain and nothing to go stale between runs. Signals reads only; nothing is written back.
Cassandra groups tables into keyspaces rather than databases. Enter the keyspace where this page asks for the database name, and the table within it as the table name.
You can point it at a table, or at a query. The query path is what you want when the shape Signals needs does not match anything you already have.
CQL has no joins and no subqueries, and a statement reads one table. Reshaping records on the way out is bounded by what that one table already holds, so where a destination needs a value kept somewhere else, the two have to be brought together before the read rather than inside it. A materialized view does not do that work: it is a re-keyed copy of a single base table, so it changes which partition key you can query by and still joins nothing. Cassandra also ships with materialized views switched off, its configuration file calling them experimental and not recommended for production use. The table you nominate is therefore the table your application writes.
Supported destinations
Section titled “Supported destinations”Destinations supported by Apache Cassandra
| Category | Supported |
|---|---|
| Offline Conversions | |
| Lead Conversions | |
| Custom Audience |
Authentication
Section titled “Authentication”In Studio, open Sources, find the Database category, and click the Apache Cassandra tile.
| Field | What it is | Where to find it |
|---|---|---|
| Authentication Name | Your own label for this set of credentials. | You choose it. Name it for the database it belongs to. |
| Username | The database user Signals connects as. | Created in the database by your administrator. |
| Password | The password for that user. | Set when the user was created. |
Cassandra only allows efficient queries against the partition key its tables were designed around. A query that filters on anything else may be refused outright rather than running slowly.
Cassandra checks no credentials at all until somebody tells it to. A stock cassandra.yaml carries authenticator: AllowAllAuthenticator and authorizer: AllowAllAuthorizer, the first admitting any connection without asking and the second handing every permission to every role, so the roles you create are written down and never consulted. PasswordAuthenticator and CassandraAuthorizer are what replace them, and the node has to be restarted before either change takes effect.
The role itself takes two statements:
CREATE ROLE datahash WITH PASSWORD = '...' AND LOGIN = true;GRANT SELECT ON KEYSPACE appks TO datahash;LOGIN = true is load-bearing. Roles arrive with neither login nor superuser status, so the same pair of statements with that clause dropped leaves a role that holds the read and is refused at connection time, told it is not permitted to log in. CREATE USER is the older spelling of the same thing with login already true, which is why examples written around it appear to manage with one clause fewer.
The system_auth keyspace is created with a replication factor of 1, and the Cassandra documentation recommends 3 to 5 per datacenter for anything past a trial. At 1, the single node holding a role decides whether anybody carrying that role can sign in, whatever the rest of the cluster is doing. Credentials for the built-in cassandra superuser are read at QUORUM during login and every other role’s at LOCAL_ONE, so the two do not fail together and getting in as cassandra proves less about your service account than it appears to.
A grant is not visible the instant it is made. roles_validity and permissions_validity both default to 2000ms, so a change can take a couple of seconds to reach a node still holding the previous answer. A verification run fired straight after a GRANT can fail on a permission that is by then correct.
Configuration
Section titled “Configuration”Instance Setup
Section titled “Instance Setup”Select the Integration Type, which is the kind of data your records hold. Offline Events is the common choice. Give the instance a name, then choose the table path or the query path. On either path you enter the Host Name, Port and Keyspace Name, and then either the Table Name or a SQL query.
The integration type decides the columns Signals expects and which destinations the connection can feed, so it is worth settling before anyone models the data. It also decides which file format you get on the next screen.
Port 9042 and how many replicas answer a read
Section titled “Port 9042 and how many replicas answer a read”9042 is native_transport_port, where Cassandra takes CQL from clients. 7000 and 7001 are storage_port and ssl_storage_port, which carry traffic between nodes and offer a client nothing, so either of those in the Port field produces an attempt that never becomes a session.
Consistency level settles how many replicas have to answer before a read returns, and it is why a conversion written seconds earlier can be absent from a run that reports no errors at all. LOCAL_ONE comes back as soon as one replica in the local datacenter answers, and that replica can be one the write has not yet reached. Raising the read on its own does not close the gap. A read is guaranteed to observe a write only where the replicas the read demands and the replicas the write demanded overlap, which inside one datacenter means the two counts have to add up to more than the replication factor. Writing at LOCAL_QUORUM and reading at LOCAL_QUORUM against a keyspace replicated three ways satisfies that; writing at LOCAL_ONE and reading at LOCAL_QUORUM does not, and the write half of that is set by your application rather than here. The level this connection reads at is not a field on the setup screen, so ask your Datahash representative which one it uses before treating a missing recent conversion as a delivery failure.
Table path
Section titled “Table path”Enter the connection details including the table name, then complete the setup. The name is typed rather than picked from a list, so it has to match the database exactly.
Query path
Section titled “Query path”Enter the connection details without the table name, then write the query. Preview the results before finishing, and read the column names in the output rather than just the row count: a query that runs but returns the wrong names will fail silently once the connection is live.
Manage instance
Section titled “Manage instance”One instance reads one table or query for one integration type. To read another, add another instance from the Manage existing instance table. To change an existing one, open it from there, click the edit option in the menu to the top right, update the fields and click Finish.
File format and schema
Section titled “File format and schema”Download the file format from the setup screen and shape the table to it. The column names are what Signals matches on, so a column that has been renamed or cased differently is not recognized and the row it belongs to is dropped rather than reported.
On the query path the same rule applies to the column aliases your query returns. Aliasing to the expected names is the usual way to reconcile data you cannot or should not restructure.
Data & identifiers
Section titled “Data & identifiers”Personal identifiers are normalized and hashed before they are sent to a destination.
Best practices
Section titled “Best practices”- Use a dedicated read-only user scoped to the data Datahash reads, rather than an application account.
- Prefer the query path with explicit column aliases over pointing at a raw table. It lets your team change the underlying model without breaking the connection.
- Ask your Datahash representative which node the connection reaches and what happens when that node restarts. Every node can coordinate a read, so there is no primary to steer this load away from, and no cluster-wide address to enter in its place either.
- Design the query around the partition key, then check every other condition you have hung off it. Cassandra resolves a restriction on the partition key, on a clustering prefix in order, or on a column a secondary index covers; a condition on any other regular column is refused until the statement adds
ALLOW FILTERING. Model a table for this read instead of adding that clause, which lets the read proceed at the cost of fetching rows to discard them. - Give the role
LOGINas well asSELECTon the one keyspace. A role granted the read and nothing else cannot connect. - Clean the data before it is written to the table Datahash reads: lowercase email, phone in E.164, no stray whitespace. CQL has no string functions to normalize a value in the
SELECTlist, so a value stored unclean is read unclean, and hashing a badly formatted value produces a hash that will never match. - Rotate the password periodically and update the connection afterwards.
Troubleshooting & FAQ
Section titled “Troubleshooting & FAQ”The connection times out rather than reporting bad credentials. That points at reachability rather than authentication. Confirm the host is exposed, the port is open, and any firewall or allow list permits the connection.
Credentials are rejected. Check the user can read the data from a client of its own first. Most failures here are permissions rather than typos.
The table name is rejected. It is typed rather than selected, so it has to match exactly.
The query previews correctly but nothing is delivered. The column names it returns do not match the expected schema. Alias them to the names in the downloaded file format.
The query is refused rather than returning slowly. One of the conditions reaches past what the partition key, the clustering prefix and the table’s indexes can resolve, and the refusal says so: Cannot execute this query as it might involve data filtering and thus may have unpredictable performance. If you want to execute this query despite the performance unpredictability, use ALLOW FILTERING. Naming the full partition key does not settle this by itself, so read the whole WHERE rather than stopping at the key: a comparison against an unindexed regular column draws the same refusal inside a single partition as it does across the table. Take the message as a warning rather than as an instruction, because appending the clause authorizes exactly the work it describes, and on a table of any size the run that follows tends to end at read_request_timeout instead, which is the harder failure to attribute for arriving intermittently. Confine the conditions to columns this table is keyed or indexed for, or model a table for this read.
A role that exists is refused at connection time. Roles are created without the LOGIN option unless the statement asks for it, and one without it is turned away with a message saying it is not permitted to log in, whatever else it holds. LIST ROLES shows the login column for every role on the cluster. ALTER ROLE datahash WITH LOGIN = true settles it without disturbing the grant, where dropping and recreating the role would discard the grant and trade this error for a permissions one.
Reads against one partition fail on and off while the rest of the table behaves. Tombstones are the usual cause. A deleted row leaves one behind, a row whose TTL has expired leaves one too, and a read has to walk past every tombstone in the partition it touches. The node logs a warning once a read passes tombstone_warn_threshold, 1000 by default, and aborts the read at tombstone_failure_threshold, 100000 by default. What the client gets back is a read failure rather than rows, while the node log carries TombstoneOverwhelmingException and the count it scanned, and that log line is the confirmation. Reads short of the abort threshold slow down instead, and read_request_timeout, 5000ms by default, is where those surface. Raising either threshold is the wrong direction, since they are there to keep a node from running out of memory. Tombstones clear through compaction, and nothing can drop them before the table’s gc_grace_seconds has elapsed, so the durable fix is a data model that does not delete or expire rows inside the partitions this read scans.
It was working and stopped. The password may have been changed or the user disabled. A dedicated service user avoids this.

