Lentelė, trigeris ir veikėjo modelis
Trys failai kortelėse: migracija, užklausos ir veikėjo modelis Go. 00006_audit_events.sql → audit.sql → audit.go.
1. db/migrations/00006_audit_events.sql (naujas) — audit_events lentelė, jos indeksas ir papildomo-tik užtikrinimas: plpgsql funkcija, kuri RAISE, ir trigeriai, sujungiantys ją su BEFORE UPDATE OR DELETE (kiekvienai eilutei) ir BEFORE TRUNCATE (kiekvienam sakiniui). Atkreipk dėmesį į seq BIGINT GENERATED ALWAYS AS IDENTITY ir be išorinio rakto esančius document_id / actor_* stulpelius.
2. internal/db/queries/audit.sql (naujas) — dvi užklausos: CreateAuditEvent (įterpti vieną eilutę) ir ListAuditEventsByDocument (ORDER BY seq). Paleisk sqlc generate.
3. internal/handlers/audit.go (naujas) — Go pusė veikėjo modelio:
evt*įvykio tipo konstantos (po vieną kiekvienam būsenos pokyčiui),auditActorir jos konstruktoriaiactorUser/actorSigner/actorSystem/actorAnonymous— kiekvienas nustatoactor_typeir užšaldytąLabel,audit(ctx, q, docID, type, message, actor)— rašo vieną eilutę. Ji priima*db.Queries, tad paduodi transakcijai susietąq(perWithTx), kad įrašytum įvykį atomiškai su būsenos pokyčiu, arbah.Queriesgeriausių-pastangų skaitymo kelio įvykiams,auditBestEffort— puslapio peržiūros įvykiams, kurie niekada neturi sužlugdyti vartotojo užklausos (jis užrašo klaidą į žurnalą, o ne ją grąžina).
func (h *Handlers) audit(ctx context.Context, q *db.Queries, docID pgtype.UUID, eventType, message string, a auditActor) error {
return q.CreateAuditEvent(ctx, db.CreateAuditEventParams{
DocumentID: docID, EventType: eventType, Message: message,
ActorType: a.Type, ActorUserID: a.UserID, ActorSignerID: a.SignerID, ActorLabel: a.Label,
})
}
Patikra — perseeduok, kad paleistum migraciją, tada bandyk sulaužyti papildomo-tik taisyklę tiesiai psql:
$ psql "$DATABASE_URL" -c "insert into audit_events (document_id, event_type, message, actor_type, actor_label)
values (gen_random_uuid(), 'test', 'hi', 'system', 'system');"
INSERT 0 1
$ psql "$DATABASE_URL" -c "update audit_events set message='changed';"
ERROR: audit_events is append-only: UPDATE is not permitted
$ psql "$DATABASE_URL" -c "delete from audit_events;"
ERROR: audit_events is append-only: DELETE is not permitted
Įterpimas veikia; atnaujinimas ir trynimas atmetami duomenų bazės, garsiai — ne programos kodo, kurį turi nepamiršti parašyti. Tai ir yra garantija. Ir pastebėk, kad dabar negali ištrinti savo paties bandymo eilutės — o tai būtent ir yra esmė. Norėdamas ją pašalinti, paleisk migracijos down (kuris numeta visą lentelę) ir migruok aukštyn iš naujo.