mcpField
E
etl-incremental-patterns

etl-incremental-patterns

github.com/majesticlabs-devデータ・データベース5066 回インストール7 週間前に更新
リポジトリ

etl-incremental-patterns について

ETL Incremental Patterns は、増分ロード、バックフィル、パイプラインのオーケストレーションに関するリファレンススキルです。データソースの機能、テーブル規模、更新要件に応じたロード戦略の選択を支援します。

主な機能

  1. 01タイムスタンプベースの増分ロード
  2. 02変更データキャプチャの処理
  3. 03日付範囲の並列バックフィル
  4. 04テーブル交換による全件更新
  5. 05パイプラインステップの状態パターン
  6. 06ロード戦略の判断マトリクス

活用シーン

  1. 01タイムスタンプを使った大規模テーブルの同期
  2. 02CDC の INSERT、UPDATE、DELETE イベントの適用
  3. 03履歴データの並列ロード
  4. 04交換パターンを使った重要テーブルの更新

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/majesticlabs-dev/majestic-marketplace --skill etl-incremental-patterns

インストール後、Claude Code で/etl-incremental-patternsを入力して読み込まれたか確認します。

詳細

ETL の増分処理パターン

増分データ読み込みとバックフィル操作のパターンです。

バックフィル戦略

from datetime import date, timedelta
from concurrent.futures import ThreadPoolExecutor, as_completed

def backfill_date_range(
    start: date,
    end: date,
    process_fn: callable,
    parallel: int = 4
) -> None:
    """Backfill data for a date range."""
    dates = []
    current = start
    while current <= end:
        dates.append(current)
        current += timedelta(days=1)

    # Process in parallel with controlled concurrency
    with ThreadPoolExecutor(max_workers=parallel) as executor:
        futures = {executor.submit(process_fn, d): d for d in dates}
        for future in as_completed(futures):
            d = futures[future]
            try:
                future.result()
                print(f"Completed: {d}")
            except Exception as e:
                print(f"Failed: {d} - {e}")

# Usage
backfill_date_range(
    start=date(2024, 1, 1),
    end=date(2024, 3, 31),
    process_fn=process_daily_data,
    parallel=4
)

増分読み込みパターン

タイムスタンプベースの増分処理
def incremental_by_timestamp(table: str, timestamp_col: str) -> pd.DataFrame:
    last_run = get_last_run_timestamp(table)
    query = f"""
        SELECT * FROM {table}
        WHERE {timestamp_col} > :last_run
        ORDER BY {timestamp_col}
    """
    df = pd.read_sql(query, engine, params={'last_run': last_run})
    if not df.empty:
        set_last_run_timestamp(table, df[timestamp_col].max())
    return df
変更データキャプチャ(CDC)
def process_cdc_events(events: list[dict]) -> None:
    for event in events:
        op = event['operation']  # INSERT, UPDATE, DELETE
        data = event['data']

        if op == 'DELETE':
            soft_delete(data['id'])
        else:
            upsert(data)
スワップによる全件リフレッシュ
def full_refresh_with_swap(df: pd.DataFrame, table: str) -> None:
    temp_table = f"{table}_temp"
    df.to_sql(temp_table, engine, if_exists='replace', index=False)

    with engine.begin() as conn:
        conn.execute(text(f"DROP TABLE IF EXISTS {table}_old"))
        conn.execute(text(f"ALTER TABLE {table} RENAME TO {table}_old"))
        conn.execute(text(f"ALTER TABLE {temp_table} RENAME TO {table}"))
        conn.execute(text(f"DROP TABLE {table}_old"))

パイプラインのオーケストレーション

from enum import Enum
from dataclasses import dataclass, field

class StepStatus(Enum):
    PENDING = "pending"
    RUNNING = "running"
    SUCCESS = "success"
    FAILED = "failed"
    SKIPPED = "skipped"

@dataclass
class PipelineStep:
    name: str
    func: callable
    dependencies: list[str] = field(default_factory=list)
    status: StepStatus = StepStatus.PENDING
    error: str | None = None

class Pipeline:
    def __init__(self, name: str):
        self.name = name
        self.steps: dict[str, PipelineStep] = {}

    def add_step(self, name: str, func: callable, depends_on: list[str] = None):
        self.steps[name] = PipelineStep(name, func, depends_on or [])

    def run(self) -> bool:
        for step in self._topological_sort():
            # Skip if dependencies failed
            if any(self.steps[d].status == StepStatus.FAILED for d in step.dependencies):
                step.status = StepStatus.SKIPPED
                continue

            step.status = StepStatus.RUNNING
            try:
                step.func()
                step.status = StepStatus.SUCCESS
            except Exception as e:
                step.status = StepStatus.FAILED
                step.error = str(e)

        return all(s.status == StepStatus.SUCCESS for s in self.steps.values())

    def _topological_sort(self) -> list[PipelineStep]:
        # Implementation of topological sort for dependency ordering
        ...

読み込み戦略の選択マトリックス

シナリオパターン使用するタイミング
小規模なテーブル(<100K 行)全件リフレッシュ日次/時間単位の読み込み
タイムスタンプのある大規模なテーブルタイムスタンプベースの増分処理継続的な同期
ソースが CDC に対応CDC イベントリアルタイム更新
1 回限りの履歴データ読み込み並列バックフィル初期移行
重要なテーブルスワップパターンダウンタイムゼロのリフレッシュ

コメント