/
mixed.go
163 lines (137 loc) · 4.77 KB
/
mixed.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package redmos
import (
"context"
"fmt"
)
// newMixed 建立混合資料庫
func newMixed(major *Major, minor *Minor) *Mixed {
return &Mixed{
major: major,
minor: minor,
}
}
// Mixed 混合資料庫, 內部用主要與次要資料庫實現混合命令, 包含以下功能
// - 取得執行物件: 取得資料庫執行器
type Mixed struct {
major *Major // 主要資料庫物件
minor *Minor // 次要資料庫物件
}
// Submit 取得執行物件
func (this *Mixed) Submit(ctx context.Context) *Submit {
return &Submit{
context: ctx,
major: this.major.Submit(),
minor: this.minor.Submit(),
}
}
// Submit 混合資料庫執行器, 執行命令時需要遵循以下流程
// - 定義包含了 Behave 的行為結構 / 定義繼承了 Behavior 的行為結構
// - 建立行為資料, 並且填寫內容(例如索引值, 資料內容, 結果成員等)
// - 取得執行物件
// - 新增行為到執行物件中
// - 執行命令 Exec
// - 檢查執行命令結果以及進行後續處理
//
// 目前已經實作了幾個預設行為 Lock, Unlock, Get, Set, Index 可以幫助使用者作行為設計;
// 其中 Lock, Unlock 已經直接整合到 Submit 提供的函式
type Submit struct {
context context.Context // ctx物件
major MajorSubmit // 主要執行物件
minor *MinorSubmit // 次要執行物件
behavior []Behavior // 行為列表
}
// Add 新增行為
func (this *Submit) Add(behavior ...Behavior) *Submit {
for _, itor := range behavior {
itor.Initialize(this.context, this.major, this.minor)
} // for
this.behavior = append(this.behavior, behavior...)
return this
}
// Lock 新增鎖定行為
func (this *Submit) Lock(key string) *Submit {
return this.Add(&Lock{Key: key, time: Timeout})
}
// Unlock 新增解鎖行為
func (this *Submit) Unlock(key string) *Submit {
return this.Add(&Unlock{Key: key})
}
// LockIf 新增鎖定行為, 依靠旗標來判斷是否要鎖定
func (this *Submit) LockIf(key string, lock bool) *Submit {
if lock {
return this.Add(&Lock{Key: key, time: Timeout})
} // if
return this
}
// UnlockIf 新增解鎖行為, 依靠旗標來判斷是否要解鎖
func (this *Submit) UnlockIf(key string, unlock bool) *Submit {
if unlock {
return this.Add(&Unlock{Key: key})
} // if
return this
}
// Exec 執行命令, 執行命令時, 會以下列順序執行
// - 執行所有行為的 Prepare 函式, 如果有錯誤就中止執行
// - 執行主要資料庫的管線處理
// - 執行所有行為的 Complete 函式, 如果有錯誤就中止執行
// - 執行次要資料庫的管線處理
// - 清除管線資料
func (this *Submit) Exec() error {
for _, itor := range this.behavior {
if err := itor.Prepare(); err != nil {
return fmt.Errorf("submit queue prepare: %w", err)
} // if
} // for
if len(this.behavior) > 0 {
_, _ = this.major.Exec(this.context)
} // if
for _, itor := range this.behavior {
if err := itor.Complete(); err != nil {
return fmt.Errorf("submit queue complete: %w", err)
} // if
} // for
if len(this.behavior) > 0 {
if err := this.minor.Exec(this.context); err != nil {
return fmt.Errorf("submit queue minor: %w", err)
} // if
} // if
this.behavior = nil
return nil
}
// Behavior 行為介面, 當建立行為時, 需要實現此介面, 建議可以把 Behave 組合進行為結構中, 可以省去初始處理的實作;
// 設計行為時, 有以下的設計規範
// - 主要資料庫的情況下: 由於主要資料庫會用管線機制執行, 因此通常會在 Prepare 新增管線命令, 然後在 Complete 檢查執行是否符合預期
// - 次要資料庫的情況下: 由於次要資料庫會以常規方式執行, 因此通常 Prepare 不會有內容, 然後在 Complete 執行資料庫命令並且檢查執行是否符合預期
// - 錯誤處理: 當資料庫失敗時才會回傳錯誤, 若是邏輯錯誤(例如資料不存在), 就不應該回傳錯誤, 而是把結果記錄下來提供外部使用
type Behavior interface {
// Initialize 初始處理
Initialize(ctx context.Context, major MajorSubmit, minor *MinorSubmit)
// Prepare 準備處理
Prepare() error
// Complete 完成處理
Complete() error
}
// Behave 行為資料
type Behave struct {
context context.Context // ctx物件
major MajorSubmit // 主要執行物件
minor *MinorSubmit // 次要執行物件
}
// Initialize 初始處理
func (this *Behave) Initialize(ctx context.Context, major MajorSubmit, minor *MinorSubmit) {
this.context = ctx
this.major = major
this.minor = minor
}
// Ctx 取得ctx物件
func (this *Behave) Ctx() context.Context {
return this.context
}
// Major 取得主要執行物件
func (this *Behave) Major() MajorSubmit {
return this.major
}
// Minor 取得次要執行物件
func (this *Behave) Minor() *MinorSubmit {
return this.minor
}