feat: add durable result journal and subscription delivery core
This commit is contained in:
52
internal/storage/mysql/result_journal.go
Normal file
52
internal/storage/mysql/result_journal.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
"git.yuntuo.hk/k2safe/orbit/internal/jsonwire"
|
||||
core "git.yuntuo.hk/k2safe/orbit/model"
|
||||
"git.yuntuo.hk/k2safe/orbit/platform/events"
|
||||
)
|
||||
|
||||
// appendResultEvent 必须在来源任务更新和事件写入之后、原事务提交之前调用。
|
||||
// 流行一直锁到提交;后来的事务无法提交更大序号并越过尚未提交的较小序号。
|
||||
// 不访问订阅或交付行:慢消费者不能反向阻塞任务的结果持久化。
|
||||
func appendResultEvent(ctx context.Context, tx *sql.Tx, ns, eventID, taskID core.ID, revision uint64, eventType string) error {
|
||||
var snapshot events.TaskResult
|
||||
var raw []byte
|
||||
snapshot.SchemaVersion = 1
|
||||
err := tx.QueryRowContext(ctx, `SELECT id,queue_id,state,revision,result,effect_uncertain,current_attempt_id FROM pulse_tasks WHERE namespace_id=? AND id=?`, ns, taskID).Scan(&snapshot.TaskID, &snapshot.QueueID, &snapshot.State, &snapshot.Revision, &raw, &snapshot.EffectUncertain, &snapshot.CurrentAttemptID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if snapshot.Revision != revision || eventType != "task."+map[string]string{"SUCCEEDED": "succeeded", "FAILED": "failed", "CANCELLED": "cancelled", "NEEDS_REVIEW": "needs_review"}[snapshot.State] {
|
||||
return errors.New("task result event does not match persisted task state")
|
||||
}
|
||||
snapshot.Result = raw
|
||||
body, err := jsonwire.Marshal(snapshot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
head, _, err := lockEventStream(ctx, tx, ns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if head == math.MaxUint64 {
|
||||
return errors.New("event sequence exhausted")
|
||||
}
|
||||
if _, err = tx.ExecContext(ctx, `INSERT INTO orbit_result_events(namespace_id,id,sequence,queue_id,snapshot) VALUES (?,?,?,?,?)`, ns, eventID, head+1, snapshot.QueueID, string(body)); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.ExecContext(ctx, `UPDATE orbit_event_streams SET last_sequence=?,updated_at=UTC_TIMESTAMP(6) WHERE namespace_id=?`, head+1, ns)
|
||||
return err
|
||||
}
|
||||
func lockEventStream(ctx context.Context, tx *sql.Tx, ns core.ID) (head, retained uint64, err error) {
|
||||
if _, err = tx.ExecContext(ctx, `INSERT INTO orbit_event_streams(namespace_id) VALUES (?) ON DUPLICATE KEY UPDATE namespace_id=orbit_event_streams.namespace_id`, ns); err != nil {
|
||||
return
|
||||
}
|
||||
err = tx.QueryRowContext(ctx, `SELECT last_sequence,retained_from_sequence FROM orbit_event_streams WHERE namespace_id=? FOR UPDATE`, ns).Scan(&head, &retained)
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user