ClickHouse storage is suitable for production environments and massive data scenarios, leveraging ClickHouse's powerful write throughput and data compression capabilities.
Soft-deleted data retention period. When enabled, periodically cleans up via ALTER TABLE DELETE. Not recommended for production — prefer ClickHouse table-level TTL
The ClickHouse implementation uses the ReplacingMergeTree engine for data updates and deduplication.
Key characteristics:
ReplacingMergeTree: Using the updated_at field, ClickHouse automatically merges records with the same primary key in the background, keeping the latest version
FINAL queries: All read operations use the FINAL keyword (e.g., SELECT ... FINAL) to ensure data parts are merged at query time for read consistency
Soft Delete: Delete operations are implemented by inserting a new record with a deleted_at timestamp; queries filter by deleted_at IS NULL
CREATETABLEIFNOTEXISTSsession_states(app_nameString,user_idString,session_idString,stateJSONCOMMENT'Session state in JSON format',extra_dataJSONCOMMENT'Additional metadata',created_atDateTime64(6),updated_atDateTime64(6),expires_atNullable(DateTime64(6))COMMENT'Expiration time (application-level)',deleted_atNullable(DateTime64(6))COMMENT'Soft delete timestamp')ENGINE=ReplacingMergeTree(updated_at)PARTITIONBY(app_name,cityHash64(user_id)%64)ORDERBY(app_name,user_id,session_id)SETTINGSallow_nullable_key=1COMMENT'Session states table';
CREATETABLEIFNOTEXISTSsession_events(app_nameString,user_idString,session_idString,event_idString,eventJSONCOMMENT'Event data in JSON format',extra_dataJSONCOMMENT'Additional metadata',created_atDateTime64(6),updated_atDateTime64(6),expires_atNullable(DateTime64(6))COMMENT'Reserved for future use',deleted_atNullable(DateTime64(6))COMMENT'Soft delete timestamp')ENGINE=ReplacingMergeTree(updated_at)PARTITIONBY(app_name,cityHash64(user_id)%64)ORDERBY(app_name,user_id,session_id,event_id)SETTINGSallow_nullable_key=1COMMENT'Session events table';
CREATETABLEIFNOTEXISTSsession_summaries(app_nameString,user_idString,session_idString,filter_keyStringCOMMENT'Filter key for multiple summaries per session',summaryJSONCOMMENT'Summary data in JSON format',created_atDateTime64(6),updated_atDateTime64(6),expires_atNullable(DateTime64(6))COMMENT'Reserved for future use',deleted_atNullable(DateTime64(6))COMMENT'Soft delete timestamp')ENGINE=ReplacingMergeTree(updated_at)PARTITIONBY(app_name,cityHash64(user_id)%64)ORDERBY(app_name,user_id,session_id,filter_key)SETTINGSallow_nullable_key=1COMMENT'Session summaries table';
CREATETABLEIFNOTEXISTSapp_states(app_nameString,keyStringCOMMENT'State key',valueStringCOMMENT'State value',updated_atDateTime64(6),expires_atNullable(DateTime64(6))COMMENT'Expiration time (application-level)',deleted_atNullable(DateTime64(6))COMMENT'Soft delete timestamp')ENGINE=ReplacingMergeTree(updated_at)PARTITIONBYapp_nameORDERBY(app_name,key)SETTINGSallow_nullable_key=1COMMENT'Application states table';
CREATETABLEIFNOTEXISTSuser_states(app_nameString,user_idString,keyStringCOMMENT'State key',valueStringCOMMENT'State value',updated_atDateTime64(6),expires_atNullable(DateTime64(6))COMMENT'Expiration time (application-level)',deleted_atNullable(DateTime64(6))COMMENT'Soft delete timestamp')ENGINE=ReplacingMergeTree(updated_at)PARTITIONBY(app_name,cityHash64(user_id)%64)ORDERBY(app_name,user_id,key)SETTINGSallow_nullable_key=1COMMENT'User states table';