ããã·ã¥é£éã«ããPostgreSQLã®æ¹ããæ€ç¥å¯èœãªç£æ»ãã¬ã€ã«
PostgreSQLã§append-onlyããŒãã«ãšããã·ã¥é£éã䜿ããã¬ãã¥ãŒãèª¿æ»æã«ç·šéãæ€åºã§ããæ¹ããæ€ç¥å¯èœãªç£æ»ãã¬ã€ã«ãåŠã³ãŸãã

éåžžã®ç£æ»ãã°ãäºç¹ã«ãªããããçç±
ç£æ»ãã¬ã€ã«ã¯ãäœããããããšæãããšãã«é Œãèšé²ã§ãïŒäžå¯©ãªè¿éã誰ãèŠããŠããªãæš©é倿Žããããã¯ãæ¶ããã顧客ã¬ã³ãŒããç£æ»ãã¬ã€ã«èªäœãç·šéã§ãããªããããã¯èšŒæ ã§ã¯ãªã誰ããæžãæããããå¯èœæ§ã®ããå¥ã®ããŒã¿ã«ãªããŸãã
å€ãã®ãç£æ»ãã°ãã¯åãªãéåžžã®ããŒãã«ã§ããè¡ãæŽæ°ã»åé€ã§ããã°ãç©èªãæŽæ°ã»åé€ãããŠããŸããŸãã
éèŠãªåºå¥ã¯ãç·šéã黿¢ããããšãšãç·šéãæ€åºå¯èœã«ããããšã¯åãã§ã¯ãªãããšããç¹ã§ããæš©éã§å€æŽãæžããããšã¯ã§ããŸãããååãªæš©éãæã€èª°ãïŒãããã¯çãŸãã管çè è³æ Œæ å ±ïŒãªãå±¥æŽãå€ããããŸããæ¹ããæ€ç¥ã¯ãã®çŸå®ãåãå ¥ããŸãããã¹ãŠã®å€æŽãé²ããªããããããŸãããã倿Žãæç¢ºãªçè·¡ãæ®ãããã«ã§ããŸãã
éåžžã®ç£æ»ãã°ãäºç¹ã«ãªãçç±ã¯äºæž¬å¯èœã§ããç¹æš©ãŠãŒã¶ãŒãäºåŸã«ãã°ããä¿®æ£ãã§ããã䟵害ãããã¢ããªã¢ã«ãŠã³ããéåžžã®ãã©ãã£ãã¯ã«èŠããä¿¡ãããããšã³ããªãæžãããã¿ã€ã ã¹ã¿ã³ããé¡ã£ãŠåããŠé 延倿Žãé ããããããã¯æãäžå©ãªè¡ã ããåé€ããããšãã§ããŸãã
ãæ¹ããæ€ç¥å¯èœããšã¯ãç£æ»ãã¬ã€ã«ãèšèšããŠãã»ãã®å°ããªç·šéïŒãã£ãŒã«ãã1ã€å€ãããè¡ã1ã€åé€ãããã€ãã³ãã®é åºãå ¥ãæ¿ããïŒãåŸã§æ€åºå¯èœã«ãªãããã«ããããšãæå³ããŸããéæ³ãçŽæããããã§ã¯ãããŸãããããã®ãã°ãæ¬ç©ã§ããããšãã©ã蚌æããã®ãïŒããšèããããšãã«ããã°ãè§ŠããããŠãããã©ããã瀺ããã§ãã¯ãå®è¡ã§ããããšããããšãçŽæããŸãã
äœã蚌æããå¿ èŠãããããæ±ºãã
æ¹ããæ€ç¥å¯èœãªç£æ»ãã¬ã€ã«ã¯ãåŸã§çŽé¢ãã質åã«çããããå Žåã«ã®ã¿æçšã§ãïŒèª°ãäœãããã®ãããã€ãããããã®ããäœãå€ãã£ãã®ãã
ãŸããããžãã¹äžéèŠãªã€ãã³ãããå§ããŸããããŒã¿å€æŽïŒäœæã»æŽæ°ã»åé€ïŒã¯åºç€ã§ããã調æ»ã¯ã»ãã¥ãªãã£ãã¢ã¯ã»ã¹ã«é¢ããåºæ¥äºã«äŸåããããšãå€ãã§ãïŒãã°ã€ã³ããã¹ã¯ãŒããªã»ãããæš©é倿Žãã¢ã«ãŠã³ãããã¯ã¢ãŠããæ¯æããè¿éãã¯ã¬ãžãããæ¯æãåŠçãæ±ãå Žåã¯ããããŒã®ç§»åã坿¬¡çãªæŽæ°ã§ã¯ãªã第äžçŽã®ã€ãã³ããšããŠæ±ã£ãŠãã ããã
次ã«ãã€ãã³ããä¿¡çšã§ãããšèŠãªãããããã®èŠçŽ ãæ±ºããŸããç£æ»äººã¯éåžžãã¢ã¯ã¿ãŒïŒãŠãŒã¶ãŒãŸãã¯ãµãŒãã¹ïŒããµãŒããŒåŽã®ã¿ã€ã ã¹ã¿ã³ããå®è¡ãããã¢ã¯ã·ã§ã³ã圱é¿ãåãããªããžã§ã¯ããæåŸ ããŸããæŽæ°ã«ã€ããŠã¯ã倿Žåãšå€æŽåŸã®å€ïŒå°ãªããšãææãªãã£ãŒã«ãïŒãä¿åããè€æ°ã®å°ããªããŒã¿ããŒã¹å€æŽã1ã€ã®ãŠãŒã¶ãŒæäœã«çµã³ã€ããããã®ãªã¯ãšã¹ãIDãçžé¢IDãæãããŸãã
æåŸã«ãã·ã¹ãã å ã§ãäžå€ããäœãæå³ããããæç¢ºã«ããŸããæãåçŽãªã«ãŒã«ã¯ïŒç£æ»è¡ãæŽæ°ãããåé€ããããããæ¿å ¥ã ããè¡ãããšã§ããäœããééã£ãŠããããå€ãã€ãã³ããèšæ£ã»äžæžãããæ°ããã€ãã³ããæžããå ã®ã€ãã³ãã¯å¯èŠã®ãŸãŸã«ããŸãã
append-onlyã®ç£æ»ããŒãã«ãäœã
ç£æ»ããŒã¿ã¯éåžžã®ããŒãã«ãšã¯åé¢ããŠãããŸããããå°çšã®auditã¹ããŒãã¯å¶çºçãªç·šéãæžãããæš©éã®èšèšãç°¡åã«ããŸãã
ç®æšã¯ç°¡åã§ãïŒè¡ã¯è¿œå ã§ãããã倿Žãåé€ã¯ã§ããªããPostgreSQLã§ã¯æš©éïŒèª°ãäœãã§ãããïŒãšããŒãã«èšèšã®ããã€ãã®å®å šçã§ããã匷å¶ããŸãã
å®çšçãªéå§ããŒãã«ã®äŸïŒ
CREATE SCHEMA IF NOT EXISTS audit;
CREATE TABLE audit.events (
id bigserial PRIMARY KEY,
entity_type text NOT NULL,
entity_id text NOT NULL,
event_type text NOT NULL CHECK (event_type IN ('INSERT','UPDATE','DELETE')),
actor_id text,
occurred_at timestamptz NOT NULL DEFAULT now(),
request_id text,
before_data jsonb,
after_data jsonb,
notes text
);
èª¿æ»æã«ç¹ã«åœ¹ç«ã€ããã€ãã®ãã£ãŒã«ãïŒ
occurred_atã¯DEFAULT now()ã䜿ããæå»ãã¯ã©ã€ã¢ã³ãã§ã¯ãªãããŒã¿ããŒã¹åŽã§å»ããentity_typeãšentity_idã«ããã1ã€ã®ã¬ã³ãŒãã®å€æŽå±¥æŽã远ãããrequest_idã«ããã1ã€ã®ãŠãŒã¶ãŒæäœãè€æ°ã®è¡ã§è¿œè·¡ã§ããã
ããŒã«ã§ç· ãä»ããŸããããã¢ããªã±ãŒã·ã§ã³çšããŒã«ã¯ audit.events ã«å¯Ÿã㊠INSERT ãš SELECT ã¯ã§ããããUPDATE ã DELETE ã¯ã§ããªãããã«ããŸããã¹ããŒã倿ŽãåŒ·ãæš©éã¯ãã¢ããªã§äœ¿ãããªã管çè
ããŒã«ã«éå®ããŸãã
ããªã¬ãŒã§å€æŽãææããïŒã·ã³ãã«ã§äºæž¬å¯èœïŒ
æ¹ããæ€ç¥å¯èœãªç£æ»ãã¬ã€ã«ãäœããªãã倿Žãææããæãä¿¡é Œã§ããå Žæã¯ããŒã¿ããŒã¹ã§ããã¢ããªã±ãŒã·ã§ã³ãã°ã¯ã¹ãããããããããã£ã«ã¿ãããããæžãæãããããããå¯èœæ§ããããŸããããªã¬ãŒã¯ãã©ã®ã¢ããªãã¹ã¯ãªããã管çããŒã«ãããŒãã«ã«è§ŠããŠãçºç«ããŸãã
ããªã¬ãŒã¯åé·ã«ããªãã§ãã ããã圹å²ã¯äžã€ã ãïŒéèŠãªããŒãã«ã®åINSERTãUPDATEãDELETEã«å¯ŸããŠ1ã€ã®ç£æ»ã€ãã³ãã远å ããããšã§ãã
å®çšçãªç£æ»ã¬ã³ãŒãã«ã¯éåžžãããŒãã«åãæäœã¿ã€ããäž»ããŒã倿ŽååŸã®å€ãã¿ã€ã ã¹ã¿ã³ããé¢é£ã¥ãã«äœ¿ããèå¥åïŒãã©ã³ã¶ã¯ã·ã§ã³IDãçžé¢IDïŒãå«ãŸããŸãã
çžé¢IDããããšã20è¡ãæŽæ°ããããã§ã¯ãªããããã¯1åã®ãã¿ã³ã¯ãªãã¯ã ã£ãããšèª¬æã§ããŸããã¢ããªã¯ãªã¯ãšã¹ãããšã«äžåºŠçžé¢IDãã»ããã§ãïŒäŸãã°DBã»ãã·ã§ã³èšå®ã§ïŒãããªã¬ãŒããããèªã¿ãŸããçžé¢IDãç¡ãå Žåã§ã txid_current() ãä¿åããŠããã°ã°ã«ãŒãã³ã°ãå¯èœã§ãã
以äžã¯ãç£æ»ããŒãã«ã«å¯ŸããŠæ¿å ¥ã ãè¡ãããšã§äºæž¬å¯èœæ§ãä¿ã€åçŽãªããªã¬ãŒãã¿ãŒã³ã§ãïŒã¹ããŒãåãååã¯ç°å¢ã«åãããŠèª¿æŽããŠãã ããïŒïŒ
CREATE OR REPLACE FUNCTION audit_row_change() RETURNS trigger AS $$
DECLARE
corr_id text;
BEGIN
corr_id := current_setting('app.correlation_id', true);
INSERT INTO audit_events(
occurred_at, table_name, op, row_pk,
old_row, new_row, db_user, txid, correlation_id
) VALUES (
now(), TG_TABLE_NAME, TG_OP, COALESCE(NEW.id, OLD.id),
to_jsonb(OLD), to_jsonb(NEW), current_user, txid_current(), corr_id
);
RETURN COALESCE(NEW, OLD);
END;
$$ LANGUAGE plpgsql;
ããªã¬ãŒã§äœèšãªããšãããããªãèªæã«æµæããŠãã ããã远å ã®ã¯ãšãªããããã¯ãŒã¯åŒã³åºããè€éãªåå²ã¯é¿ããŸããããå°ããªããªã¬ãŒã¯ãã¹ããããããå®è¡ãéããã¬ãã¥ãŒæã«äºç¹ã«ãªãã«ããã§ãã
ç·šéãæçŽãæ®ãããã«ããã·ã¥é£éã远å ãã
append-onlyããŒãã«ã ãã§ã广ã¯ãããŸãããååãªæš©éãæã€èª°ãã¯éå»ã®è¡ãæžãæããããå¯èœæ§ããããŸããããã·ã¥é£éã¯ãã®çš®ã®æ¹ãããå¯èŠåããŸãã
åç£æ»è¡ã«2ã€ã®åã远å ããŸãïŒprev_hash ãš row_hashïŒchain_hash ãšåŒã°ããããšããããŸãïŒãprev_hash ã¯åããã§ãŒã³äžã®åã®è¡ã®ããã·ã¥ãä¿åããrow_hash ã¯çŸåšã®è¡ã®ããŒã¿ãš prev_hash ããèšç®ããããã·ã¥ãä¿åããŸãã
äœãããã·ã¥ããããéèŠã§ããåãè¡ãåžžã«åãããã·ã¥ãçããããå®å®ããåçŸå¯èœãªå ¥åãçšæãããã§ãã
å®çšçãªæ¹æ³ã¯ãåºå®åïŒã¿ã€ã ã¹ã¿ã³ããã¢ã¯ã¿ãŒãã¢ã¯ã·ã§ã³ããšã³ãã£ãã£IDïŒããäœãæ£èŠåãããæååãæ£èŠåããããã€ããŒãïŒå€ã㯠jsonbãããŒé ãäžè²«ããããïŒãš prev_hash ãçµã¿åãããŠããã·ã¥åããããšã§ãã
空çœãJSONããŒé ããã±ãŒã«äŸåã®æžåŒãªã©ãæå³ã®ãªãå·®åã«æ³šæããŠãã ãããåãäžè²«ããã1ã€ã®äºæž¬å¯èœãªæ¹æ³ã§ã·ãªã¢ã©ã€ãºããŸãã
å šããŒã¿ããŒã¹åäœã§ã¯ãªãã¹ããªãŒã ããšã«ãã§ãŒã³ãã
å šã€ãã³ãã1ã€ã®ã°ããŒãã«ãªé£ç¶ã§ãã§ãŒã³ãããšæžã蟌ã¿ãããã«ããã¯ã«ãªãåŸãŸããå€ãã®ã·ã¹ãã ã¯ããã³ãããšããšã³ãã£ãã£ã¿ã€ãããšãæ¥åãªããžã§ã¯ãããšã®ããã«ãã¹ããªãŒã ãåäœã§ãã§ãŒã³ããŸãã
æ°ããè¡ã¯èªåã®ã¹ããªãŒã ã®ææ° row_hash ãåç
§ããŠããã prev_hash ã«ä¿åããããããèªåã® row_hash ãèšç®ããŸãã
-- Requires pgcrypto
-- digest() returns bytea; store hashes as bytea
row_hash = digest(
concat_ws('|',
stream_key,
occurred_at::text,
actor_id::text,
action,
entity,
entity_id::text,
payload::jsonb::text,
encode(prev_hash, 'hex')
),
'sha256'
);
ãã§ãŒã³ã®ããããã¹ãããã·ã§ãããã
ã¬ãã¥ãŒãéãããããã«ãææ°ã® row_hashïŒããã§ãŒã³ããããïŒãã¹ããªãŒã ããšã«å®æçã«å°ããªã¹ãããã·ã§ããããŒãã«ã«ä¿åããŸãïŒããšãã°æ¥æ¬¡ïŒãèª¿æ»æã«ã¯å
šå±¥æŽãäžåºŠã«èµ°æ»ãã代ããã«åã¹ãããã·ã§ãããŸã§ãã§ãŒã³ãæ€èšŒã§ããŸããã¹ãããã·ã§ããã¯ãšã¯ã¹ããŒããæ¯èŒããŠäžå¯©ãªã®ã£ãããèŠã€ããã®ã«ã䟿å©ã§ãã
ãã§ãŒã³ãå£ããã«åæå®è¡ãšé åºãæ±ã
å®éçšäžã§ã¯ããã·ã¥é£éã¯åä»ã§ãã2ã€ã®ãã©ã³ã¶ã¯ã·ã§ã³ãåæã«ç£æ»è¡ãæžããäž¡æ¹ãåã prev_hash ã䜿ããšãã©ãŒã¯ãçºçããå¯èœæ§ããããŸããããã¯åäžã®ã¯ãªãŒã³ãªã·ãŒã±ã³ã¹ã蚌æããåã匱ããŸãã
ãŸãããã§ãŒã³ãäœã衚ããŠããã®ããæ±ºããŠãã ãããã°ããŒãã«ãã§ãŒã³ã¯èª¬æãæãç°¡åã§ããç«¶åãæãæ¿ãããªããŸããè€æ°ãã§ãŒã³ã¯ç«¶åãæžãããŸãããåãã§ãŒã³ãäœã蚌æããããæç¢ºã«ããŠããå¿ èŠããããŸãã
ã©ã®ã¢ãã«ãæ¡ãã«ãããå調å¢å ããã€ãã³ãIDïŒéåžžã¯ã·ãŒã±ã³ã¹ã§ä»äžãããIDïŒã§å³å¯ãªé åºãå®çŸ©ããŠãã ãããã¿ã€ã ã¹ã¿ã³ãã ãã§ã¯è¡çªãããããæäœã§æäœããåŸãããååã§ã¯ãããŸããã
prev_hash ãèšç®ããéã®ç«¶åãé¿ããã«ã¯ãåã¹ããªãŒã ã§ãæåŸã®ããã·ã¥ãååŸ + 次ã®è¡ãæ¿å
¥ãã®æäœãçŽååããŸããäžè¬çãªææ³ã¯ãã¹ããªãŒã ãããã衚ãåäžè¡ãããã¯ããããã¹ããªãŒã IDãããŒã«ããã¢ããã€ã¶ãªããã¯ã䜿ãããšã§ããç®çã¯ãåäžã¹ããªãŒã ã®2人ã®ã©ã€ã¿ãŒãåãæåŸã®ããã·ã¥ãèªããªãããã«ããããšã§ãã
ããŒãã£ã·ã§ã³ãã·ã£ãŒãã£ã³ã°ã¯ãæåŸã®è¡ããã©ãã«ãããã«åœ±é¿ããŸããç£æ»ããŒã¿ãããŒãã£ã·ã§ã³ããäºå®ããããªããã¹ããªãŒã ããŒãšåãããŒãã£ã·ã§ã³ããŒã䜿ã£ãŠåãã§ãŒã³ã1ã€ã®ããŒãã£ã·ã§ã³å ã«å®å šã«ä¿æããŠãã ããïŒäŸïŒããã³ãIDïŒãããããã°ãããã³ããã§ãŒã³ã¯åŸã§ãµãŒããŒéãç§»åããŠãæ€èšŒå¯èœãªãŸãŸã§ãã
èª¿æ»æã«ãã§ãŒã³ãæ€èšŒããæ¹æ³
ããã·ã¥é£éã¯ã誰ããå°ãããšãã«ãã§ãŒã³ãä¿æãããŠããããšã蚌æã§ããŠåããŠåœ¹ã«ç«ã¡ãŸããæãå®å šãªæ¹æ³ã¯èªã¿åãå°çšã®æ€èšŒã¯ãšãªïŒãŸãã¯ãžã§ãïŒã§ãåè¡ã®ããã·ã¥ãæ ŒçŽããŒã¿ããåèšç®ããèšé²ãããããã·ã¥ãšæ¯èŒããããšã§ãã
ãªã³ããã³ãã§å®è¡ã§ããã·ã³ãã«ãªæ€èšŒåš
æ€èšŒåšã¯ãåè¡ã®æåŸ ãããããã·ã¥ãåæ§ç¯ããåè¡ãåã®è¡ã«ãªã³ã¯ããŠãããã確èªããäœãåé¡ãããã°ãã©ã°ãç«ãŠãã¹ãã§ãã
以äžã¯ãŠã£ã³ããŠé¢æ°ã䜿ã£ãäžè¬çãªãã¿ãŒã³ã§ããååã¯èªç°å¢ã«åãããŠèª¿æŽããŠãã ããã
WITH ordered AS (
SELECT
id,
created_at,
actor_id,
action,
entity,
entity_id,
payload,
prev_hash,
row_hash,
LAG(row_hash) OVER (ORDER BY created_at, id) AS expected_prev_hash,
/* expected row hash, computed the same way as in your insert trigger */
encode(
digest(
coalesce(prev_hash, '') || '|' ||
id::text || '|' ||
created_at::text || '|' ||
coalesce(actor_id::text, '') || '|' ||
action || '|' ||
entity || '|' ||
entity_id::text || '|' ||
payload::text,
'sha256'
),
'hex'
) AS expected_row_hash
FROM audit_log
)
SELECT
id,
created_at,
CASE
WHEN prev_hash IS DISTINCT FROM expected_prev_hash THEN 'BROKEN_LINK'
WHEN row_hash IS DISTINCT FROM expected_row_hash THEN 'HASH_MISMATCH'
ELSE 'OK'
END AS status
FROM ordered
WHERE prev_hash IS DISTINCT FROM expected_prev_hash
OR row_hash IS DISTINCT FROM expected_row_hash
ORDER BY created_at, id;
ãå£ããŠãããã©ããã以å€ã«ããã®ã£ããïŒIDã®ç¯å²ã«æ¬ èœããªããïŒãé åºã®ä¹±ããå®éã®ã¯ãŒã¯ãããŒãšäžèŽããªãçãããéè€ãªã©ããã§ãã¯ãã䟡å€ããããŸãã
æ€èšŒçµæãäžå€ã€ãã³ããšããŠèšé²ãã
ã¯ãšãªãå®è¡ããŠçµæããã±ããã«åããã ãã§ã¯ãªããæ€èšŒçµæã¯å¥ã®append-onlyããŒãã«ïŒäŸïŒaudit_verification_runsïŒã«ã©ã³ã¿ã€ã ãæ€èšŒåšã®ããŒãžã§ã³ãå®è¡è
ãæ€æ»ããç¯å²ãå£ãããªã³ã¯æ°ãããã·ã¥äžäžèŽæ°ãªã©ãšãšãã«ä¿åããŠãã ããã
ããã«ããäºéã®ãã¬ã€ã«ãã§ããŸãïŒç£æ»ãã°ãç¡å·ã§ããã ãã§ãªããããã宿çã«ãã§ãã¯ããŠããããšã瀺ããŸãã
å®çšçãªé »åºŠãšããŠã¯ãç£æ»ããžãã¯ã«è§Šãããããã€åŸãã¢ã¯ãã£ããªã·ã¹ãã ã§ã¯å€éããããŠèšç»ãããç£æ»ã®åã«ã¯å¿ ãå®è¡ããããªã©ãèããããŸãã
æ¹ããæ€ç¥ãå£ãäžè¬çãªå€±æ
ã»ãšãã©ã®å€±æã¯ããã·ã¥ã¢ã«ãŽãªãºã èªäœã§ã¯ãªããäŸå€ãã®ã£ããã«ãã£ãŠäººã ãåè«ã®äœå°ãæã€ç¹ã«ãããŸãã
ä¿¡é Œã倱ãæéã®æ¹æ³ã¯ç£æ»è¡ã®æŽæ°ãèš±ãããšã§ããäžåºŠã§ããä»åã¯ããã ãããšèš±ããšãåäŸãšå±¥æŽãæžãæããããã®å®éã®éçãäœã£ãŠããŸããŸããä¿®æ£ãå¿ èŠãªããæ°ããç£æ»ã€ãã³ãã远å ããŠãã®ä¿®æ£ã説æããå ã®è¡ã¯æ®ããŠãã ããã
ããã·ã¥é£éã¯äžå®å®ãªããŒã¿ãããã·ã¥ãããšå€±æããŸããJSONã¯ããããèœãšã穎ã§ããJSONæååãããã·ã¥ãããšãããŒé ã空çœãæ°å€ã®æžåŒãªã©ç¡å®³ãªå·®åã§ããã·ã¥ãå€ãããæ€èšŒããã€ãžãŒã«ãªããŸããæ£èŠåããã圢ïŒãã£ãŒã«ãã®æ£èŠåãjsonbããŸãã¯äžè²«ããã·ãªã¢ã©ã€ãºïŒã奜ãã§ãã ããã
é²åŸ¡å¯èœãªãã¬ã€ã«ãæãªãä»ã®ãã¿ãŒã³ïŒ
- ãã€ããŒãã ããããã·ã¥ããŠã³ã³ããã¹ãïŒã¿ã€ã ã¹ã¿ã³ããã¢ã¯ã¿ãŒããªããžã§ã¯ãIDãã¢ã¯ã·ã§ã³ïŒãçãã
- 倿Žãã¢ããªåŽã§ããææãããããŒã¿ããŒã¹ãåžžã«äžèŽãããšä»®å®ããã
- ããžãã¹ããŒã¿ãæžã蟌ã¿ãåæã«ç£æ»å±¥æŽã倿Žã§ãã1ã€ã®ããŒã¿ããŒã¹ããŒã«ã䜿ãã
- ãã§ãŒã³å
ã§
prev_hashã NULL 蚱容ã«ããŠæç¢ºãªã«ãŒã«ãææžåããŠããªãã
è·ååé¢ã¯éèŠã§ããåãããŒã«ãç£æ»ã€ãã³ããæ¿å ¥ã§ãããã€ããã倿Žã§ãããšãæ¹ããæ€ç¥ã¯å¶åŸ¡ã§ã¯ãªãçŽæã«ãªã£ãŠããŸããŸãã
é²åŸ¡å¯èœãªç£æ»ãã¬ã€ã«ã®ããã®ã¯ã€ãã¯ãã§ãã¯ãªã¹ã
é²åŸ¡å¯èœãªç£æ»ãã¬ã€ã«ã¯å€æŽãã«ãããæ€èšŒããããã¹ãã§ãã
ã¢ã¯ã»ã¹å¶åŸ¡ããå§ããŸãããïŒç£æ»ããŒãã«ã¯å®åäžappend-onlyã«ããããšãã¢ããªã±ãŒã·ã§ã³ããŒã«ã¯æ¿å ¥ïŒéåžžã¯èªã¿åããïŒã§ãããæŽæ°ãåé€ã¯ã§ããªãããã«ããã¹ããŒã倿Žã¯å³ããå¶éããŸãã
åè¡ã調æ»è ã®è³ªåã«çããããããã«ããŸãïŒèª°ãè¡ã£ããããã€ïŒãµãŒããŒåŽã¿ã€ã ã¹ã¿ã³ãïŒãäœãèµ·ãããïŒæç¢ºãªã€ãã³ãåãšæäœïŒãäœã圱é¿ãåãããïŒãšã³ãã£ãã£åãšIDïŒãã©ãã€ãªãããïŒrequest/correlation idãštransaction idïŒã
æ¬¡ã«æŽåæ§ã¬ã€ã€ãŒãæ€èšŒããŸããç°¡åãªãã¹ãã¯ã»ã°ã¡ã³ããåçããå prev_hash ãåè¡ã®ããã·ã¥ãšäžèŽããããä¿åãããããã·ã¥ãåèšç®ããããã®ãšäžèŽãããã確èªããããšã§ãã
éçšé¢ã§ã¯ãæ€èšŒãéåžžã®ãžã§ããšããŠæ±ã£ãŠãã ããïŒ
- 宿çãªæŽåæ§ãã§ãã¯ãå®è¡ããååŠçµæãšç¯å²ãä¿åããã
- äžäžèŽãã®ã£ãããå£ãããªã³ã¯ãã¢ã©ãŒãåããã
- ä¿ææéãã«ããŒããããã«ããã¯ã¢ãããååã«ä¿æããç£æ»å±¥æŽãæ©æã«ãã¯ãªãŒã³ã¢ããããããªãããä¿æããªã·ãŒãããã¯ããŠã³ããã
äŸïŒã³ã³ãã©ã€ã¢ã³ã¹ã¬ãã¥ãŒã§çãããç·šéãèŠã€ãã
ããããã±ãŒã¹ã¯è¿éã®äºãã§ãã顧客ã$250ã®è¿éãæ¿èªããããšäž»åŒµããããã·ã¹ãã äžã¯$25ã«ãªã£ãŠããããµããŒãã¯æ¿èªãæ£ãããšèšããã³ã³ãã©ã€ã¢ã³ã¹ã¯èª¬æãæ±ããŸãã
ãŸãçžé¢IDïŒæ³šæIDããã±ããIDããŸã㯠refund_request_idïŒãšæéçªã§æ€çŽ¢ç¯å²ãçµããŸãããã®çžé¢IDã®ç£æ»è¡ãåãåºããæ¿èªæéã®ååŸãç¯å²ã«å
¥ããŸãã
æ¢ãã¹ãã¯ããªã¯ãšã¹ãäœæãè¿éæ¿èªãè¿éé¡ã®èšå®ããããŠãã®åŸã®æŽæ°ã®ãã«ã»ããã§ããæ¹ããæ€ç¥èšèšãããã°ãã·ãŒã±ã³ã¹ãå®å šã«ä¿ãããŠãããããã§ãã¯ããŸãã
ã·ã³ãã«ãªèª¿æ»ãããŒïŒ
- çžé¢IDã§ç£æ»è¡ãæéé ã«å šãŠåãåºãã
- åè¡ã®ããã·ã¥ãä¿åããããã£ãŒã«ãïŒ
prev_hashãå«ãïŒããåèšç®ããã - åèšç®ããããã·ã¥ãšä¿åãããããã·ã¥ãæ¯èŒããã
- æåã«ç°ãªãè¡ãç¹å®ãããã®åŸã®è¡ã倱æããŠãããã確èªããã
ãã誰ãã1è¡ãç·šéããŠïŒããšãã°éé¡ã250ãã25ã«å€ããïŒããå Žåããã®è¡ã®ããã·ã¥ã¯äžèŽããªããªããŸããæ¬¡ã®è¡ã¯åã®ããã·ã¥ãå«ãããããäžäžèŽã¯éåžžåæ¹ãžé£éããŸãããã®é£éãæçŽã§ãïŒäºåŸã«ç£æ»èšé²ãæ¹ãããããããšã瀺ããŸãã
ãã§ãŒã³ãæããŠãããããšïŒç·šéãçºçããããšããã§ãŒã³ãæåã«å£ããå Žæã圱é¿ãåããè¡ã®ç¯å²ããã§ãŒã³ã ãã§ã¯æããŠãããªãããšïŒèª°ãç·šéããããäžæžããããå Žåã®å ã®å€ãä»ã®ããŒãã«ãåæ§ã«å€æŽããããã©ããã
次ã®ã¹ãããïŒå®å šã«å±éããã¡ã³ãããã«ã«ä¿ã€
ç£æ»ãã¬ã€ã«ãä»ã®ã»ãã¥ãªãã£ã³ã³ãããŒã«ãšåæ§ã«æ±ã£ãŠãã ãããå°ããã¹ãããã§ããŒã«ã¢ãŠãããåäœã確èªããŠããç¯å²ãæ¡å€§ããŸãã
ãŸããäºç¹ã«ãªã£ããšãã«æã害ã倧ããæäœãã«ããŒããŸãããïŒæš©é倿Žãæ¯æããè¿éãããŒã¿ãšã¯ã¹ããŒããæåãªãŒããŒã©ã€ããããããã«ããŒããããã³ã¢èšèšãå€ããã«äœãªã¹ã¯ãªã€ãã³ãã远å ããŠãã ããã
ç£æ»ã€ãã³ãã®å¥çŽãæžãåºããŠãã ããïŒã©ã®ãã£ãŒã«ããèšé²ããããåã€ãã³ãã¿ã€ãã®æå³ãããã·ã¥ã®èšç®æ¹æ³ãæ€èšŒã®å®è¡æ¹æ³ããããã®ããã¥ã¡ã³ããããŒã¿ããŒã¹ãã€ã°ã¬ãŒã·ã§ã³ã®æšªã«çœ®ããæ€èšŒæé ãåçŸå¯èœã«ä¿ã¡ãŸãã
ãªã¹ãã¢ã®æŒç¿ã¯éèŠã§ãã調æ»ã¯ãã°ãã°ã©ã€ãã·ã¹ãã ã§ã¯ãªãããã¯ã¢ããããå§ãŸãããã宿çã«ãã¹ãããŒã¿ããŒã¹ã«åŸ©å ãããã§ãŒã³ã®ç«¯ãã端ãŸã§æ€èšŒããŠãã ããã埩å åŸã«åãæ€èšŒçµæãåçŸã§ããªãå Žåãæ¹ããæ€ç¥ã¯æ£åœæ§ã䞻匵ãã«ãããªããŸãã
å éšããŒã«ã管çã¯ãŒã¯ãããŒãAppMaster (appmaster.io) ã§æ§ç¯ããŠãããªãããµãŒããŒåŽåŠçã§ç£æ»ã€ãã³ãã®æžã蟌ã¿ãæšæºåããããšã§ãã€ãã³ãã¹ããŒããšçžé¢IDãæ©èœéã§äžè²«ããããã«ãªããæ€èšŒã調æ»ããã£ãšç°¡åã«ãªããŸãã
ãã®ã·ã¹ãã ã®ããã«ã¡ã³ããã³ã¹æéã確ä¿ããŠãã ãããããŒã ãæ°æ©èœãåºãéã«ã€ãã³ã远å ãããã·ã¥å ¥åã®æŽæ°ãæ€èšŒãžã§ãããªã¹ãã¢æŒç¿ã®ç¶ç¶ãå¿ãããšãç£æ»ãã¬ã€ã«ã¯éãã«å€±æããŸãã


