data-scale · Stage 3

アクセスパターンと制約からストレージADRを再計算する

query shape、不変条件、更新競合、成長予測を証拠に、relational・document・key-valueモデルを同じ基準で比較する。

学習時間
300分
難易度
intermediate
更新日
2026-07-30
到達証拠
成果物・説明・判断根拠・転用

到達目標

  1. query shape、同時に読むdata、頻度、不変条件、更新競合、成長予測を、ストレージ製品とは独立したworkload契約として記述できる

    • query shape、不変条件、更新競合、成長予測、同一criteriaでの三モデル比較、負の帰結、confirmationを含む機械可読ストレージADR
    • data modelを製品名やschemaの有無だけで選べない理由を説明する6分間の解説
  2. relational・document・key-valueモデルをaccess fit、constraint、capacity、operations、recoveryの同じ加重基準で比較し、観測証拠からscoreを再計算できる

    • query shape、不変条件、更新競合、成長予測、同一criteriaでの三モデル比較、負の帰結、confirmationを含む機械可読ストレージADR
    • constraint・capacity・operations・recoveryの証拠を用いてratingを反証し、access pattern変更後のscoreを再計算する判断記録
  3. アクセスパターンの前提変更で選択肢のratingとwinnerを再計算し、負の帰結と確認方法を含むADRを更新できる

    • constraint・capacity・operations・recoveryの証拠を用いてratingを反証し、access pattern変更後のscoreを再計算する判断記録
    • 注文検索を顧客中心から商品・期間中心へ変え、変更した前提、再計算結果、移行と回復条件を記録したADR

能力の進行

  1. recognize

    entity、value、relationship、query shape、不変条件、更新競合、成長予測を識別できる

    証拠: query shape、不変条件、更新競合、成長予測、同一criteriaでの三モデル比較、負の帰結、confirmationを含む機械可読ストレージADR

  2. explain

    論理data modelと物理配置を分け、relational・document・key-valueの適合条件と限界を説明できる

    証拠: data modelを製品名やschemaの有無だけで選べない理由を説明する6分間の解説

  3. apply

    同じweightとcriteriaで三モデルを評価し、constraint、capacity、operations、recoveryの証拠をADRへ接続できる

    証拠: query shape、不変条件、更新競合、成長予測、同一criteriaでの三モデル比較、負の帰結、confirmationを含む機械可読ストレージADR

  4. diagnose

    schema-lessや単一table、partitionを万能解とする誤診を、workloadと運用証拠から反証できる

    証拠: constraint・capacity・operations・recoveryの証拠を用いてratingを反証し、access pattern変更後のscoreを再計算する判断記録

  5. lead

    access pattern変更時にrating、winner、移行境界、confirmationを再評価するreviewを主導できる

    証拠: 注文検索を顧客中心から商品・期間中心へ変え、変更した前提、再計算結果、移行と回復条件を記録したADR

なぜ重要か

data modelの選択は製品カテゴリ当てではない。利用者がどのdataを一緒に読み、どの不変条件を一度に守り、どこで更新が競合し、件数がどう増えるかを明示して初めて、storageの適合性を比較できる。初期のqueryには合う設計も、主要なaccess patternが変われば最適ではなくなる。

ADRの役割は永久に正しい製品を宣言することではなく、判断時のworkload、同じ比較基準、負の帰結、確認方法を残すことである。logical model、indexやpartition keyなどのphysical design、製品固有機能を分けると、前提変更時にどこから再評価すべきかを追跡できる。

メンタルモデル

最初にdomainのentity、value、relationship、不変条件を記述し、次にquery shapeへ「頻度」と「同時に読むdata」を付ける。さらに更新競合と成長予測を加えれば、access pathの局所最適化が整合性や復旧を壊さないかを比較できる。

注文履歴workloadを比較するdecision table
modelaccess fitconstraintoperations・recovery主な負の帰結
relational顧客履歴とad-hoc条件をindex・joinで扱う一意性・参照・transaction境界を宣言するmigration、backup、point-in-time recoveryを演習するjoin、index維持、scale-up上限を測る必要
document注文aggregateを一緒に読むqueryへ適合validationとapplication invariantを併用document肥大、index、restoreを監視する重複と部分更新競合、横断queryの追加index
key-value既知keyの固定queryを低いfan-outで読む複数key不変条件の責任を明示するhot key、再構築、backup復旧を演習する二次access pathと再同期の複雑性

vendor scope: relationalの例はPostgreSQL 18のdata definition、documentの例はMongoDB 8.0のdata modeling、key-valueの機構は2007年Dynamo論文modelに限定する。製品version間の機能差やmanaged serviceのSLAを一般化しない。

fixtureの由来: 注文件数、query frequency、rating evidenceはlesson用に作ったsynthetic scenarioであり、production traceや実vendor benchmarkではない。実判断では匿名化したquery log、cardinality、latency分位、更新競合、backup・restore演習で置き換える。

workload証拠からstorage ADRを再計算するmechanism

注記

図を読む際の補足情報です。

  1. domain: 注文番号の一意性、合計額と明細合計の一致、注文状態遷移を不変条件にする。
  2. workload: 顧客履歴、注文detail、商品・期間検索、状態更新を一意なquery IDと時間当たり頻度で表す。
  3. growth: 現在件数と月間増加から12か月後を計算し、synthetic projectionである限界を残す。
  4. comparison: relational、document、key-valueをaccess fit、constraint、capacity、operations、recoveryの同じweightで採点する。
  5. ADR: winnerだけでなく、負の帰結、移行境界、confirmationを記録する。
  6. mutation: 商品・期間検索が主要queryになったらfrequencyだけを変える。query-fit ratingは固定し、access-fit rating、weighted score、winnerを再計算する。
  7. worked exampleでは全optionへ同じweight set 0.45/0.20/0.15/0.10/0.10を適用する。query-fit ratingは固定し、frequency変更からaccess-fit ratingを導出する。
  8. baseline total 4.288957; winner relational; changed total 3.668868; winner key-value

query frequencyだけを変え、固定したquery-fit ratingから何を再計算するか。

判断マトリクス
項目access fit × 0.45query frequency×model別ratingの加重平均。baselineと変更後を同じ式で再計算する。constraint × 0.20不変条件と更新競合のratingとscore contribution。capacity × 0.151.1M projected recordsへのtested pathのratingとcontribution。operations × 0.10migration・observe・operateのratingとcontribution。recovery × 0.10backup・restore・reconstructionのratingとcontribution。
relational関係・constraint・transactionを明示する候補。baseline (480×5 + 360×3 + 48×1 + 90×4) / 978 = 3.975460; changed (24×5 + 360×3 + 480×1 + 90×4) / 954 = 2.138365; weight 0.45rating 5 × 0.20 = 1.00rating 4 × 0.15 = 0.60rating 4 × 0.10 = 0.40rating 5 × 0.10 = 0.50; baseline total 4.288957; changed total 3.462264
documentaggregate単位で一緒に読むdataを保持する候補。baseline (480×4 + 360×5 + 48×2 + 90×4) / 978 = 4.269939; changed (24×4 + 360×5 + 480×2 + 90×4) / 954 = 3.371069; weight 0.45rating 3 × 0.20 = 0.60rating 4 × 0.15 = 0.60rating 3 × 0.10 = 0.30rating 3 × 0.10 = 0.30; baseline total 3.721472; changed total 3.316981
key-valuekeyによるbounded lookupを中心にする候補。baseline (480×2 + 360×4 + 48×5 + 90×2) / 978 = 2.883436; changed (24×2 + 360×4 + 480×5 + 90×2) / 954 = 4.264151; weight 0.45rating 2 × 0.20 = 0.40rating 5 × 0.15 = 0.75rating 2 × 0.10 = 0.20rating 4 × 0.10 = 0.40; baseline total 3.047546; changed total 3.668868

changed inputはfrequencyだけで、query-fit ratingは固定され、導出されるaccess-fit rating、weighted score、winnerが順に変わり得ることを説明できる。

動く例で考える

顧客中心から商品・期間中心へADRを更新する

前提
現在500,000注文、月50,000件増、12か月予測のsynthetic workloadを使う。baselineでは顧客履歴が毎時480回の主要queryであり、商品・期間検索は毎時48回ある。
入力
4つのquery shape、3つの不変条件、2つの更新競合と、合計weight 1.0の5 criteriaを入力する。三モデルには同じcriteria IDで1〜5のratingを与える。
操作
各queryのfrequencyとmodel別query fitの積からaccess fitを加重平均し、他criteriaのratingとweightを合計してbaseline winnerをADRへ記録する。その後、顧客履歴を毎時24回、商品・期間検索を毎時480回へ変え、同じ式で全optionのscoreを再計算する。
観測
baselineではrelationalが最大scoreとなる。変更後は固定された商品・期間keyのread modelに適合するkey-valueが最大となり、winnerが変わる。constraint、hot key、再構築、dual-read照合は未解決の運用責任として残る。
結論
変更後winnerを即時置換の指示にしない。source of truth、secondary read model、backfill、dual-read差分、rollback、restore演習をADR confirmationにして段階的に検証する。

次のPython 3.13 harnessは標準libraryの固定値だけを使い、network、secret、shell、filesystemへアクセスしない。access pattern mutationを同じcriteriaと計算式で再評価する。

python3.13 - <<'PY'
import json

HARNESS = "storage_decision_lab_v1"
QUERY_FREQUENCIES = {
    "get_customer_history": 480,
    "get_order_detail": 360,
    "list_product_window": 48,
    "update_order_state": 90,
}

QUERY_DEFINITIONS = [
    {
        "id": "get_customer_history",
        "accessed_together": ["order_id", "placed_at", "total", "status"],
    },
    {
        "id": "get_order_detail",
        "accessed_together": ["order", "line_items", "payment_summary"],
    },
    {
        "id": "list_product_window",
        "accessed_together": ["product_id", "placed_at", "order_id", "quantity"],
    },
    {
        "id": "update_order_state",
        "accessed_together": ["order_id", "current_state", "next_state", "version"],
    },
]

WORKLOAD = {
    "query_shapes": [
        {
            **definition,
            "frequency_per_hour": QUERY_FREQUENCIES[definition["id"]],
        }
        for definition in QUERY_DEFINITIONS
    ],
    "invariants": [
        "order_id is unique",
        "order total equals the sum of line item amounts",
        "order state follows the allowed transition graph",
    ],
    "update_conflicts": [
        "payment confirmation and cancellation update the same order version",
        "shipment allocation and line cancellation update overlapping line items",
    ],
}

GROWTH_INPUTS = {
    "current_records": 500_000,
    "monthly_growth": 50_000,
    "months": 12,
}
GROWTH_FORECAST = {
    **GROWTH_INPUTS,
    "projected_records": (
        GROWTH_INPUTS["current_records"]
        + GROWTH_INPUTS["monthly_growth"] * GROWTH_INPUTS["months"]
    ),
    "evidence_kind": "synthetic-projection",
}

CRITERIA = {
    "access_fit": {
        "weight": 0.45,
        "question": "Do primary query shapes have bounded read fan-out?",
    },
    "constraint_coverage": {
        "weight": 0.20,
        "question": "Where are uniqueness, references, and transitions enforced?",
    },
    "capacity": {
        "weight": 0.15,
        "question": "Does the model have a tested path to projected volume?",
    },
    "operations": {
        "weight": 0.10,
        "question": "Can the team migrate, observe, and operate it?",
    },
    "recovery": {
        "weight": 0.10,
        "question": "Can backup, restore, and reconstruction meet the target?",
    },
}

# Query fit stays independent of frequency so every workload mutation is
# recomputed through one auditable weighted average instead of a scenario table.
QUERY_FIT_RATINGS = {
    "relational": {
        "get_customer_history": 5,
        "get_order_detail": 3,
        "list_product_window": 1,
        "update_order_state": 4,
    },
    "document": {
        "get_customer_history": 4,
        "get_order_detail": 5,
        "list_product_window": 2,
        "update_order_state": 4,
    },
    "key-value": {
        "get_customer_history": 2,
        "get_order_detail": 4,
        "list_product_window": 5,
        "update_order_state": 2,
    },
}

BASELINE_INPUTS = [
    {
        "id": "relational",
        "ratings": {
            "constraint_coverage": 5,
            "capacity": 4,
            "operations": 4,
            "recovery": 5,
        },
        "constraint_evidence": [
            "synthetic DDL maps order uniqueness, references, and state checks",
        ],
        "capacity_evidence": [
            "projected 1.1M rows fit the lab capacity envelope; measure indexes in production",
        ],
        "operations_evidence": [
            "team rehearsal covers online index creation and reversible migration",
        ],
        "recovery_evidence": [
            "point-in-time restore and constraint validation are confirmation steps",
        ],
    },
    {
        "id": "document",
        "ratings": {
            "constraint_coverage": 3,
            "capacity": 4,
            "operations": 3,
            "recovery": 3,
        },
        "constraint_evidence": [
            "document validation covers shape; cross-document invariants need explicit ownership",
        ],
        "capacity_evidence": [
            "order documents need growth and index-size limits before adoption",
        ],
        "operations_evidence": [
            "schema versioning and rolling migration are not yet rehearsed",
        ],
        "recovery_evidence": [
            "restore must also validate denormalized fields and index rebuild",
        ],
    },
    {
        "id": "key-value",
        "ratings": {
            "constraint_coverage": 2,
            "capacity": 5,
            "operations": 2,
            "recovery": 4,
        },
        "constraint_evidence": [
            "single-key conditions are local; multi-key invariants need a tested protocol",
        ],
        "capacity_evidence": [
            "synthetic partition distribution stays bounded at projected volume",
        ],
        "operations_evidence": [
            "secondary access path rebuild and hot-key response are not rehearsed",
        ],
        "recovery_evidence": [
            "reconstruction is deterministic but requires source snapshot retention",
        ],
    },
]

def rating_inputs_for(workload):
    return {
        query["id"]: query["frequency_per_hour"]
        for query in workload["query_shapes"]
    }

def score_options(option_inputs, workload):
    rating_inputs = rating_inputs_for(workload)
    total_frequency = sum(rating_inputs.values())
    options = []
    for option_input in option_inputs:
        query_fit_ratings = dict(QUERY_FIT_RATINGS[option_input["id"]])
        query_contributions = {
            query_id: frequency * query_fit_ratings[query_id]
            for query_id, frequency in rating_inputs.items()
        }
        access_fit = sum(query_contributions.values()) / total_frequency
        ratings = {
            **option_input["ratings"],
            "access_fit": access_fit,
        }
        score = sum(
            rating * CRITERIA[criterion_id]["weight"]
            for criterion_id, rating in ratings.items()
        )
        options.append(
            {
                **option_input,
                "ratings": dict(ratings),
                "query_fit_ratings": query_fit_ratings,
                "query_contributions": query_contributions,
                "score": score,
            }
        )
    return options

def selected_option(options):
    return max(options, key=lambda option: option["score"])["id"]

def evaluate_workload(workload):
    # A shared evaluator prevents baseline and transfer scenarios from drifting
    # into different scoring rules while still exposing every derived value.
    rating_inputs = rating_inputs_for(workload)
    dominant_query_id = max(rating_inputs, key=rating_inputs.__getitem__)
    options = score_options(BASELINE_INPUTS, workload)
    return {
        "workload": workload,
        "rating_inputs": rating_inputs,
        "dominant_query_id": dominant_query_id,
        "workload_profile": f"{dominant_query_id}-dominant",
        "derived_access_fit_ratings": {
            option["id"]: option["ratings"]["access_fit"]
            for option in options
        },
        "options": options,
        "selected_option": selected_option(options),
    }

def mutate_access_pattern(workload):
    changed_frequencies = {
        **{
            query["id"]: query["frequency_per_hour"]
            for query in workload["query_shapes"]
        },
        "get_customer_history": 24,
        "list_product_window": 480,
    }
    return {
        **workload,
        "query_shapes": [
            {
                **query,
                "frequency_per_hour": changed_frequencies[query["id"]],
            }
            for query in workload["query_shapes"]
        ],
    }

def mutate_order_detail(workload):
    changed_frequencies = {
        **rating_inputs_for(workload),
        "get_order_detail": 3_600_000,
    }
    return {
        **workload,
        "query_shapes": [
            {
                **query,
                "frequency_per_hour": changed_frequencies[query["id"]],
            }
            for query in workload["query_shapes"]
        ],
    }

query_ids = [query["id"] for query in WORKLOAD["query_shapes"]]
assert len(query_ids) == len(set(query_ids))
assert all(
    query["frequency_per_hour"] > 0 and query["accessed_together"]
    for query in WORKLOAD["query_shapes"]
)
assert abs(sum(item["weight"] for item in CRITERIA.values()) - 1.0) < 1e-9
assert GROWTH_FORECAST["projected_records"] == (
    GROWTH_FORECAST["current_records"]
    + GROWTH_FORECAST["monthly_growth"] * GROWTH_FORECAST["months"]
)
assert all(
    set(query_ids) == set(query_fit_ratings)
    for query_fit_ratings in QUERY_FIT_RATINGS.values()
)

baseline = evaluate_workload(WORKLOAD)
mutated_workload = mutate_access_pattern(WORKLOAD)
access_pattern_mutation = evaluate_workload(mutated_workload)
order_detail_mutation = evaluate_workload(mutate_order_detail(WORKLOAD))

assert baseline["dominant_query_id"] == "get_customer_history"
assert baseline["selected_option"] == "relational"
assert access_pattern_mutation["dominant_query_id"] == "list_product_window"
assert access_pattern_mutation["selected_option"] == "key-value"
assert (
    access_pattern_mutation["selected_option"]
    != baseline["selected_option"]
)
assert order_detail_mutation["dominant_query_id"] == "get_order_detail"
assert (
    order_detail_mutation["selected_option"] == "document"
), "order-detail-causal-invariant"
assert next(
    query["frequency_per_hour"]
    for query in mutated_workload["query_shapes"]
    if query["id"] == "list_product_window"
) == 480

report = {
    "fixture": "order-history-workload-v1",
    "fixture_metadata": {
        "kind": "synthetic",
        "provenance": "lesson-authored deterministic order-history scenario",
        "limitations": [
            "not a production query trace",
            "not a vendor benchmark or availability claim",
            "ratings require replacement with measured workload and restore evidence",
        ],
    },
    "workload": WORKLOAD,
    "growth_forecast": GROWTH_FORECAST,
    "criteria": CRITERIA,
    "baseline": baseline,
    "adr": {
        "status": "accepted-for-lab",
        "context": "customer-history queries dominate the synthetic baseline",
        "decision": baseline["selected_option"],
        "negative_consequences": [
            "secondary indexes add write and migration cost",
            "joins and index growth require measured latency and capacity evidence",
        ],
        "confirmation": [
            "validate invariants with generated conflicting updates",
            "measure query latency at projected records",
            "complete backup restore and reversible migration rehearsal",
        ],
    },
    "access_pattern_mutation": {
        **access_pattern_mutation,
        "changed_assumption": "access-pattern",
        "decision_recomputed": (
            access_pattern_mutation["selected_option"]
            != baseline["selected_option"]
        ),
        "migration_evidence": [
            "backfill the product-window read model from an immutable snapshot",
            "compare dual-read result sets before routing traffic",
            "retain relational source and replay cursor for rollback",
        ],
    },
    "order_detail_mutation": {
        **order_detail_mutation,
        "changed_assumption": "dominant-query",
        "decision_recomputed": (
            order_detail_mutation["selected_option"]
            != baseline["selected_option"]
        ),
    },
    "vendor_scope": {
        "PostgreSQL": "18",
        "MongoDB": "8.0",
        "Dynamo": "2007-paper-model",
    },
    "mastery_evidence": {
        "lab_steps": [
            {"step": 1, "evidence": "four unique query shapes, invariants, and update conflicts"},
            {"step": 2, "evidence": "synthetic 12-month projected record calculation"},
            {"step": 3, "evidence": "five weighted criteria and evidence for all three models"},
            {"step": 4, "evidence": "computed baseline scores, winner, and ADR confirmation"},
            {"step": 5, "evidence": "mutated workload, recomputed ratings, winner, and migration evidence"},
        ],
        "assessments": [
            {"assessment": 1, "evidence": "document growth, conflict, constraint, index, and restore rebuttal"},
            {"assessment": 2, "evidence": "frequency, score, dual-read, backfill, and rollback update"},
        ],
        "rubric_dimensions": [
            "technical-correctness",
            "judgment",
            "evidence",
            "communication",
        ],
        "transfer": {
            "task": "注文検索が顧客中心から商品・期間中心へ変わるアクセスパターン変更でストレージADRを再評価する",
            "changed_assumption": "access-pattern",
            "evidence": "winner changes only after workload and all option scores are recomputed",
        },
    },
    "external_network_used": False,
}

print(json.dumps(report, ensure_ascii=False, sort_keys=True))
PY

トレードオフと失敗モード

  • 誤診: document databaseならschema設計もmigrationも不要である。反証: 保存documentのfield、型、version、validation、reader互換性は変化する。古いversionを含むfixtureでrolling migrationとrollbackを実行し、未知fieldと欠損fieldの扱いを観測する。
  • 誤診: key-valueのsingle-tableへ統合すれば、すべてのaccess patternが常に最速になる。反証: 既知key以外のqueryは二次index、fan-out、複製read modelが必要になりうる。queryごとのread数、hot key分布、write amplification、再構築時間を測る。
  • 誤診: relational tableをpartitionすれば、成長後の全queryが自動的に速くなる。反証: predicateがpartition keyを絞らなければpruningできず、partition数やglobal constraintが運用費を増やす。実query planと分位latency、maintenance時間を変更前後で比較する。
  • constraintの局所化: 一つのaggregateで守れる不変条件と、複数record・keyをまたぐ不変条件を分ける。後者をapplicationへ移すなら、競合時のprotocolとreconciliationをtestする。
  • 容量の錯覚: 総record数だけではhot partition、index size、document肥大、write burstを説明できない。分布と増加率を含む複数scenarioで安全余裕を決める。
  • 運用・回復: backupが存在することと、要求時間内に整合した状態へrestoreできることは別である。schema version、secondary index、read modelを含めて定期的に復旧を演習する。

知識チェック

  1. product名を挙げる前に、query shapeへ最低限どの情報を付けるべきか。
  2. relational、document、key-valueを同じcriteriaとweightで比べる理由は何か。
  3. document modelで「schemaがない」という主張を、どのartifactとtestで反証できるか。
  4. projected recordsが同じでも、query分布の変更でwinnerが変わるのはなぜか。
  5. 商品・期間read modelへ移す際、backfill完了だけを成功条件にできない理由は何か。
  6. ADR confirmationへconstraint、capacity、operations、recoveryの証拠を一つずつ挙げる。

出典と次の学習

relational modelの原理はCoddの論文、PostgreSQL固有のdata definitionはversion 18の公式資料、document modelingはMongoDB 8.0の公式資料、key-valueの機構は2007年Dynamo論文で確認する。本文中に外部resourceを埋め込まず、完全なtitle、URL、kindはlesson metadataへ分離した。

次は「トランザクション・分離・整合性」で、同じ不変条件が並行実行時にどう破れるかを決定的scheduleで再現する。その後「性能・容量設計」でsynthetic projectionを負荷曲線、bottleneck証拠、安全容量へ接続する。

復習: 1日後にquery shapeと不変条件、7日後に三モデルの比較、30日後にaccess pattern mutation、90日後に実projectのADRを同じcriteriaで再評価する。

実践ラボ

注文履歴workloadから三モデルのADRを更新する

提出成果物: アクセスパターン、整合性、成長予測を含むストレージADR

  1. synthetic注文履歴fixtureについて、一意なquery shape ID、頻度、同時に読むdata、不変条件、更新競合を記述する
  2. 現在件数、月間増加、予測月数からprojected recordsを計算し、synthetic projectionの由来と限界を記録する
  3. access fit、constraint coverage、capacity、operations、recoveryのweightを固定し、relational・document・key-valueへ観測可能なrating evidenceを付ける
  4. 同じcriteriaから三案の加重score、winner、負の帰結、confirmationを計算し、ストレージADRを生成する
  5. 注文検索を顧客中心から商品・期間中心へ変更し、query frequency、rating、winner、移行・回復条件を再計算する

説明して理解を確かめる

6分で、document databaseがschema-lessではない理由、single-table設計が常に勝つわけではない理由、partition追加だけで全queryが速くならない理由を、query shape、不変条件、運用、回復の証拠から説明する。

アセスメント

  1. 問い: 注文detailを一つのdocumentへ埋め込めばjoinが消えて常に速い、という提案をどう評価するか。

    期待する証拠: 同時に読むdata、document成長上限、部分更新競合、重複、constraint、index、実測latency、回復手順による反証

  2. 問い: 商品・期間中心の検索頻度が顧客履歴を上回った。既存ADRのどこを更新し、移行をどう安全に確認するか。

    期待する証拠: access-pattern前提、query frequency、同一criteriaのratingとscore、winner、dual-read照合、backfill、rollback、confirmationの再計算

別問題へ転用する

注文検索が顧客中心から商品・期間中心へ変わるアクセスパターン変更でストレージADRを再評価する

復習スケジュール

  1. 1日後

    query shape一つについて、同時に読むdata、頻度、不変条件、更新競合を列挙する

  2. 7日後

    三モデルを同じcriteriaで比べ、最有力案の負の帰結とconfirmationを示す

  3. 30日後

    access pattern変更でratingとwinnerを再計算し、移行とrollback条件を説明する

  4. 90日後

    query shape一つについて、同時に読むdata、頻度、不変条件、更新競合を列挙する

評価ルーブリック

4段階の評価基準
観点未達発展途上熟達卓越
technical-correctness製品カテゴリだけを選び、query shape、不変条件、更新競合を記述しないアクセスパターンはあるが、成長予測またはconstraint・回復境界が抜けるquery shape、不変条件、更新競合、成長予測を論理modelと物理配置へ対応付けるindex、partition key、二次access path、移行中の整合性、復旧演習まで検証可能にする
judgmentschema-less、join不要、single-tableなど一つの標語だけで選ぶ複数案は比較するが、criteriaのweightやrating evidenceが揃わない三案を同じ基準で比較し、負の帰結と前提変更時の再評価条件を残す可逆性、teamの運用能力、vendor差、将来の不確実性を段階移行へ反映する
evidencebenchmark名や平均値だけを示し、fixtureとworkloadが不明であるsynthetic結果はあるが、由来、限界、constraint、recoveryの証拠が不足する固定fixtureからquery、成長、score、winner、ADRを決定的に再生成するproduction query trace、容量傾向、restore演習を匿名化し、ADR confirmationへ継続接続する
communication製品名だけを結論にし、対象versionと判断範囲を示さない選択理由はあるが、代替案、負の帰結、owner、再評価条件が曖昧である前提、criteria、選択肢、score、帰結、confirmation、vendor scopeを一つのADRで追えるdomain・開発・運用・security担当が同じ証拠から移行と撤退条件を合意できる

出典

以下の外部資料は利用者が選択したときだけ開きます。