Skip to content

feat(pipelines/cdp): finalize customer data platform with sessionization and customer 360 profiles - #274

Open
iht wants to merge 4 commits into
GoogleCloudPlatform:mainfrom
iht:feat/cdp-sessionization-pipeline
Open

feat(pipelines/cdp): finalize customer data platform with sessionization and customer 360 profiles#274
iht wants to merge 4 commits into
GoogleCloudPlatform:mainfrom
iht:feat/cdp-sessionization-pipeline

Conversation

@iht

@iht iht commented Sep 7, 2026

Copy link
Copy Markdown
Member

Description

This PR finalizes the Customer Data Platform (CDP) solution guide pipeline (pipelines/cdp/) to 100% completion by implementing dynamic event-time sessionization and Customer 360 customer journey reconstruction.

Key Changes

  1. Dynamic Event-Time Sessionization:

    • Replaced the fixed 60-second window join with Apache Beam's dynamic Sessions(gap_size) windowing (default: 15 minutes / 900 seconds).
    • Applied watermark triggers with late-firing counts (AfterWatermark(late=AfterCount(1))) and AccumulationMode.ACCUMULATING to safely incorporate late-arriving events.
    • Added AssignEventTimestampDoFn to assign timestamps from event_timestamp or Pub/Sub metadata.
  2. Customer 360 Session Profile Aggregation:

    • Added ProcessCustomerSessionDoFn which emits:
      • Main output: Granular item purchases unified with session ID, store, retail discount, coupon UPC, and marketing campaign.
      • Tagged output (sessions): Customer 360 session rollups including session duration, total spend, total items, total discounts, coupon redemption count, distinct products, stores visited, and engaged campaigns.
  3. Dead-Letter Queue (DLQ):

    • Added ParseRecordDoFn with isolated error routing via tagged output errors.
    • Diverts malformed JSON and missing-key records directly into cdp_dataset.cdp_deadletter.
  4. BigQuery Storage Write API Dual Sinks:

    • Sinks both unified items and session profiles directly into BigQuery tables (unified_customer_data and customer_sessions) via STORAGE_WRITE_API.
  5. Realistic Session Data Simulator:

    • Enhanced generate_transaction_data.py with multi-event session journey generation, supporting --continuous, --interval, --count, and --inject_errors.
  6. Local & Dataflow Execution:

    • Added scripts/02_run_local.sh for DirectRunner local execution.
    • Updated scripts/02_run_dataflow.sh with subscription arguments, session table, DLQ table, and Storage Write API options.
  7. Terraform Infrastructure:

    • Updated terraform/cdp/ with customer_sessions and cdp_deadletter BigQuery tables and updated environment variable exports in 00_set_environment.sh.
  8. Tests & Quality Verification:

    • Expanded test suite in tests/test_customer_data_platform.py to 11 unit and pipeline integration tests (100% pass rate).
    • PyLint score: 10.00/10 against pipelines/pylintrc.
    • Packaging: Verified python setup.py sdist bundles all schema files.
    • Terraform: terraform fmt -check and terraform validate passing cleanly.
  9. Promotion to Ready:

    • Promoted Customer Data Platform from Beta :factory: to Ready :white_check_mark: in README.md, updated use_cases/CDP.md, pipelines/cdp/README.md, and deployment runbooks.

TAG=agy
CONV=69d55818-9356-47fa-9e0d-cd9e5b0bfe03

iht added 2 commits September 7, 2026 15:42
…ion and customer 360 profiles

- Implement dynamic event-time sessionization using Sessions(gap_sec) with watermark-based late-data triggers
- Add ProcessCustomerSessionDoFn to produce granular items with session IDs and aggregate Customer 360 session profiles
- Implement Dead-Letter Queue (DLQ) tagged side output to isolate malformed payloads and missing keys
- Add high-throughput BigQuery Storage Write API dual sinks (unified_customer_data and customer_sessions) plus DLQ table (cdp_deadletter)
- Enhance generate_transaction_data.py to simulate realistic multi-event customer shopping journeys with --continuous, --interval, --count, and --inject_errors
- Add DirectRunner local execution script (02_run_local.sh) and update 02_run_dataflow.sh with subscription and sink parameters
- Update Terraform module in terraform/cdp to provision customer_sessions and cdp_deadletter tables and dynamic environment variables
- Expand test suite to 11 unit and pipeline integration tests with 100% pass rate and 10.00/10 PyLint rating
- Promote Customer Data Platform solution guide from Beta to Ready status

TAG=agy
CONV=69d55818-9356-47fa-9e0d-cd9e5b0bfe03
…s with Beam schemas

- Introduce strongly-typed NamedTuple models in cdp_pipeline/models.py with native Beam schema compatibility: TransactionItem, CouponRedemption, CustomerInteractionEvent, DeadLetterRecord, UnifiedTransactionRecord, and CustomerSessionProfile.
- Use EventType(StrEnum) for event categorization without string duplication.
- Refactor ParseRecordDoFn to emit composed CustomerInteractionEvent and typed deadletter side-outputs.
- Refactor ProcessCustomerSessionDoFn to operate over typed NamedTuples and produce UnifiedTransactionRecord and CustomerSessionProfile.
- Optimize BigQuery streaming sinks to use Storage Write API with auto-sharding and 5-second triggering frequency.
- Update test suite with 100% pass rate and add comprehensive unit tests for models and Beam schema compatibility.

TAG=agy
CONV=69d55818-9356-47fa-9e0d-cd9e5b0bfe03
@iht

iht commented Sep 7, 2026

Copy link
Copy Markdown
Member Author

Update: Migrated to Composable NamedTuples with Native Beam Schemas

  • Strongly Typed Models: Implemented TransactionItem, CouponRedemption, CustomerInteractionEvent, DeadLetterRecord, UnifiedTransactionRecord, and CustomerSessionProfile in cdp_pipeline/models.py.
  • Enum Categorization: Used EventType(StrEnum) (TRANSACTION, COUPON) for type-safe event parsing.
  • Composition over Duplication: Composed granular items inside CustomerInteractionEvent with automatic Beam Row schema inference.
  • BigQuery Storage Write API: Configured with with_auto_sharding=True and triggering_frequency=5 for maximum streaming performance.
  • Validation:
    • pytest tests/ -v: 20 / 20 passed (added dedicated test cases for Beam schema generation and model parsing).
    • pylint: 10.00 / 10.
    • yapf: 100% formatted.
    • terraform validate: Valid.

Comment on lines +704 to +707
output_schema: Optional[Union[Dict[str, Any], str]] = None,
):
"""Launches the Customer Data Platform streaming pipeline on Dataflow or DirectRunner."""
del output_schema # Handled via options or schema loader

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this necessary? Passing some argument that is not used and then deleting that.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! You're completely right. That parameter was leftover from an older schema-passing prototype and is no longer needed since schema loading is handled internally in build_pipeline. Removed output_schema and the del statement in commit bc1db97.

iht added 2 commits September 8, 2026 09:06
…eate_and_run_pipeline

- Remove unused output_schema parameter and del statement from create_and_run_pipeline.
- Schema loading is dynamically handled inside build_pipeline from pipeline options and packaged schema definitions.

TAG=agy
CONV=69d55818-9356-47fa-9e0d-cd9e5b0bfe03
…ata simulator

- Split 673-line customer_data_platform.py into cohesive, single-responsibility modules:
  - cdp_pipeline/schemas.py: BigQuery schema loading and defaults
  - cdp_pipeline/parsing.py: ParseRecordDoFn and AssignEventTimestampDoFn
  - cdp_pipeline/sessionization.py: ProcessCustomerSessionDoFn, legacy joins
  - cdp_pipeline/sinks.py: BigQuery Storage Write API sink builders
  - cdp_pipeline/pipeline.py: build_pipeline DAG assembly and create_and_run_pipeline
  - cdp_pipeline/customer_data_platform.py: Backward-compatible facade re-exporting all symbols
- Extract data generation out of Beam worker package into standalone simulator:
  - simulator/generator.py: Synthetic shopping basket and event generation
  - simulator/publisher.py: Async Pub/Sub streaming publisher
  - scripts/03_publish_events.py: Standardized CLI launcher script
  - cdp_pipeline/generate_transaction_data.py: Backward-compatible shim forwarding to simulator
- Add unit tests for simulator and facade export parity (24/24 tests passing).
- Maintain 10.00/10 PyLint score and full compliance with Google Python style.

TAG=agy
CONV=69d55818-9356-47fa-9e0d-cd9e5b0bfe03
@iht

iht commented Sep 8, 2026

Copy link
Copy Markdown
Member Author

Update: Codebase Modularization & Simulator Extraction

  • Decomposed Monolithic Pipeline: Split 673-line customer_data_platform.py into single-responsibility modules:
    • cdp_pipeline/schemas.py (231 lines): Schema definitions and loader
    • cdp_pipeline/parsing.py (90 lines): ParseRecordDoFn and AssignEventTimestampDoFn
    • cdp_pipeline/sessionization.py (218 lines): ProcessCustomerSessionDoFn and session aggregation
    • cdp_pipeline/sinks.py (108 lines): BigQuery Storage Write API sinks with auto-sharding
    • cdp_pipeline/pipeline.py (137 lines): DAG construction (build_pipeline) and runner
    • cdp_pipeline/customer_data_platform.py (52 lines): Thin facade re-exporting all symbols for 100% backward compatibility
  • Extracted Data Simulator: Moved out of the Beam worker package into a dedicated simulator/ package and scripts/:
    • simulator/generator.py (125 lines): Pure synthetic session basket and event generator
    • simulator/publisher.py (156 lines): Async Pub/Sub publisher CLI
    • scripts/03_publish_events.py (31 lines): Operational CLI launcher
    • cdp_pipeline/generate_transaction_data.py (36 lines): Backward-compatible shim
  • Quality & Verification:
    • pytest tests/ -v: 24 / 24 passed (added simulator and facade parity tests)
    • pylint: 10.00 / 10 across all modules
    • yapf: fully compliant
    • python setup.py sdist: clean distribution build

Comment on lines +30 to +35
warnings.warn(
"cdp_pipeline.generate_transaction_data has moved to "
"simulator.publisher and scripts/03_publish_events.py.",
DeprecationWarning,
stacklevel=2,
)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this, we are developing this from scratch, there is no need for backwards compatibility.

"""
A data generator for the Customer Data Platform analytics pipeline.
"""
"""Backward-compatible entry point forwarding to simulator.publisher."""

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need for backwards compatibility

import os
from typing import Any, Dict, Optional, Union

DEFAULT_OUTPUT_SCHEMA: Dict[str, Any] = {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The schemas are also available in JSON, are these schemas duplicated from the JSON files?

Comment on lines +216 to +218
"""Loads a BigQuery schema from a custom path, packaged file, or fallback dict."""
if fallback_schema is None:
fallback_schema = DEFAULT_OUTPUT_SCHEMA

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to add this? We fully control this project. Is it necessary to duplicate the schemas in JSON and Python code?

Comment thread pipelines/cdp/main.py
dataflow_options.job_name = f"customer-data-platform-{now_epoch_ms}"
custom_options: MyPipelineOptions = pipeline_options.view_as(
MyPipelineOptions)
if not custom_options.project_id and dataflow_options.project:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should not be a custom option for project, we use the same project id as for Dataflow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant