Skip to content
Last updated

Real Time Identity Log Tables

Real-time ID stitching decides which incoming IDs belong to which profile. The identity log tables record each of those decisions as a queryable row, so when a profile looks wrong you can find the reason in SQL: which key failed validation, which IDs pulled a merge together, and which IDs a profile lost at its association limit.

Two tables carry this record. The id_changes table logs every change to the identity graph, including evictions at the 200-association limit. The validation_failures table logs key/value pairs on incoming events that failed validation and were therefore excluded from stitching. Both are written through the standard ingest pipeline, so rows are typically queryable within a minute of the event that produced them.

Use this page as a reference for the columns in each table and the queries that answer the most common questions. For the Datadog dashboards covering throughput and errors, see Real Time Observability and Logs. For the activations table, see Reviewing Real Time Triggered Activation Logs.

What Gets Logged, and When

The id_changes and validation_failures tables record identity changes and exceptions, not a running trace of healthy traffic. Knowing which table receives a row — and when nothing is written at all — is what makes them diagnostic.

Situation Table What It Tells You
An event creates, merges, or removes a profileid_changesThe identity-graph history: which profile changed and how
An event stitches to a profile holding 200 associationsid_changesWhich IDs were evicted to make room
A key/value pair on an incoming event fails validationvalidation_failuresWhich pairs were rejected, and the reason for each
Configuration is correct and no limit is reachedNeitherNothing is written — a quiet validation_failures table means your keys are valid

A healthy, stable configuration produces no validation failures and no evictions. Rows accumulating in validation_failures signal that something upstream — a tracking implementation, a bulk upload, a key name — needs fixing. They are not a normal operating condition.

Identity logging on the event-processing path is enabled for every instance and requires no opt-in. Logging on the personalization request path is handled separately; see Personalization Path Logging.

Live Traffic Is Never Blocked

Real-time key validation never rejects a live request. An event carrying an invalid key is still processed, because refusing it would slow or break the page the visitor is on. The invalid pair is excluded from stitching, the rest of the event proceeds, and a row is written to validation_failures.

The id_changes Table

Each row in id_changes records one change to the identity graph. The profile_change_type column identifies which kind of change it was, and determines which of the other columns are populated.

Column Type Description
timeintWhen the row was emitted, as Unix epoch seconds. Filter it with TD_INTERVAL or TD_TIME_RANGE.
event_typestringAlways id for rows in this table.
reactor_instance_idstringThe real-time instance that emitted the row.
profile_change_typestringThe kind of change. See Profile Change Types.
current_ridstringThe profile the change happened on, as a UUID.
current_id_attributesarray<string>The IDs carried by the triggering event that were not already on the profile, as keyname:value strings.
key_valuesstringThe profile's full association set after the change, comma-separated. NULL on profile_ids_evicted rows.
merged_ridsarray<string>The profiles absorbed into current_rid by the merge. Populated on profile_updated_by_stitching, NULL otherwise.
evicted_idsstringA JSON array of the IDs removed to stay within the 200-association limit. Populated on profile_ids_evicted, NULL on every other row.
td_rt_tracking_idstringThe tracking ID from the originating event, when the event carried one. Otherwise null.

Profile Change Types

Value Meaning
profile_addedThe event matched no existing profile, so a new one was created.
profile_updated_by_stitchingThe event matched an existing profile, which absorbed the incoming IDs. merged_rids lists the profiles folded in.
profile_deleted_by_stitchingThe profile was removed because a merge folded it into another profile.
profile_ids_evictedAdding the incoming IDs would have exceeded 200 associations, so IDs were evicted. evicted_ids lists them.

Exactly one of profile_added, profile_updated_by_stitching, or profile_deleted_by_stitching is written per triggering event, since a profile can only be created, updated, or deleted at one time. A profile_ids_evicted row is written in addition to the profile_updated_by_stitching row for the same event, and both carry the same current_id_attributes — that shared value is how you pair them.

Reading a Merge

current_id_attributes holds only the IDs the triggering event contributed that were new to the profile — not every ID that ended up on it. That distinction is what lets you tell why a merge happened, rather than only that it happened. key_values holds the full set afterwards.

The 200-Association Limit

A profile stores at most 200 associations, counting both merged profiles and IDs. When an incoming event would push a profile past that limit, the lowest-priority associations are evicted to make room and a profile_ids_evicted row records what was removed.

Eviction can discard IDs the profile already held or IDs the incoming event brought — an incoming ID is not guaranteed to win a slot. Both kinds appear together in evicted_ids.

Repeated evictions on the same current_rid are the strongest available evidence of over-stitching, where an ID graph rule stitches on a value shared across users and collapses unrelated people into one profile. A profile churning at the limit is not storing a coherent identity, so treat these rows as a signal to review the rule that produced them.

The validation_failures Table

Each row in validation_failures covers one incoming event. If that event carried several invalid pairs, all of them appear in a single row rather than one row per pair.

Column Type Description
timeintWhen the row was emitted, as Unix epoch seconds. Filter it with TD_INTERVAL or TD_TIME_RANGE.
event_typestringAlways validation for rows in this table.
reactor_instance_idstringThe real-time instance that emitted the row.
invalid_keysarray<string>One element per rejected pair. Each element is a JSON object with key, value, and reason — for example {"value":"12345","key":"customer_id","reason":"Value is not a string."}. Unnest the array, then extract the fields with json_extract_scalar.
td_rt_tracking_idstringThe tracking ID from the originating event, when the event carried one. Otherwise null.

Validation Failure Reasons

The reason field on each entry in invalid_keys holds one of the following messages.

Reason What To Fix
Key is not present in the config file.The event sent a key name your real-time configuration does not define. Add the key to the configuration, or stop sending it.
Key name case does not match a configured key.The key name matches a configured key except for capitalization — for example User_ID against a configured user_id. Correct the case in your tracking code.
Value is not a string.The value arrived as a number, boolean, or object. Send identity values as strings; a numeric ID must be quoted.
Value is empty.The value was empty, null, or absent. Usually a tracking script reading an attribute before it is populated.
Value does not meet the valid_regexp for this key.The value failed the valid_regexp defined on that key. Either the data is malformed or the pattern is too strict.
Value is included in invalid_texts for this key.The value matched an entry in invalid_texts for that key — typically a placeholder such as null or undefined sent as a literal string.

Where the Tables Live

The id_changes and validation_failures tables are in your parent segment's real-time database, cdp_audience_<parent_segment_id>_rt, alongside the activations table. Query them from Data Workbench > Queries > New Query, using the Trino engine.

When an event carries a td_rt_tracking_id, that value appears on every row the event produced, so one value follows an event across its activation, identity, and validation outcomes. See Real Time Observability Event Tracking for how to attach it.

For ready-made diagnostic queries against these tables, see AI Skills for Real-Time Debugging.

Managing Log Volume and Retention

The id_changes and validation_failures tables hold ingested rows that persist indefinitely by default. Because id_changes records every identity-graph change, a large bulk ID graph upload writes rows in proportion to the profiles it touches, and a misconfigured tracking implementation can fill validation_failures at the rate it sends events.

Two things keep this in check:

  • Fix the underlying problem. Validation failures and evictions both represent outcomes you do not want. Once the misconfiguration is corrected, the rows stop being written.
  • Set an expiration period on the table. Configure per-table expiry so rows older than your investigation window are purged automatically. See Expiring Data From a Table.
Expiry Is Not Configured For You

Neither table has a retention period by default. Decide how far back your investigations need to reach before setting one — a short window suits validation failures, while identity-graph history in id_changes is often worth keeping longer.

Personalization Path Logging

Identity logging on the personalization request path is controlled separately from the event-processing path, and is off by default. Emitting these rows during a personalization request adds work to a latency-sensitive path, so it is enabled per instance rather than universally. Contact Customer Support if you need it turned on for an investigation.

Next Steps