w-orm-mongodb 1.1.42 → 1.1.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -3
- package/dist/w-orm-mongodb.umd.js +2 -2
- package/dist/w-orm-mongodb.umd.js.map +1 -1
- package/docs/WOrmMongodb.html +181 -13
- package/docs/WOrmMongodb.mjs.html +210 -2
- package/docs/index.html +2 -2
- package/g-basic.mjs +7 -1
- package/g-gfs.mjs +7 -1
- package/package.json +1 -1
- package/src/WOrmMongodb.mjs +208 -0
- package/test/api-basic.test.mjs +200 -0
- package/test/api-insertbulk-rs.test.mjs +290 -0
- package/test/lib/api-setup.mjs +93 -12
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import assert from 'assert'
|
|
2
|
+
import mongodb from 'mongodb'
|
|
3
|
+
import WOrm from '../src/WOrmMongodb.mjs'
|
|
4
|
+
import { genUrlRs, startContainerRs, stopContainer } from './lib/api-setup.mjs'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
//insertBulk之全有全無有兩條實作路徑, 依部署而異:
|
|
8
|
+
//具replica set或分片叢集者以交易包覆, standalone無交易可用而以補償動作達成
|
|
9
|
+
//本檔專測交易路徑, 故另起replica set容器; 補償路徑見api-basic.test.mjs之insertBulk
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
let ctName = 'worm-test-mongodb-rs'
|
|
13
|
+
let ctDb = 'worm'
|
|
14
|
+
let ctPort = null //容器對外埠, 由startContainerRs動態取得
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
//genOpt
|
|
18
|
+
let genOpt = (cl) => {
|
|
19
|
+
return {
|
|
20
|
+
url: genUrlRs(ctPort),
|
|
21
|
+
db: ctDb,
|
|
22
|
+
cl,
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//genKeys
|
|
27
|
+
let genKeys = (v) => {
|
|
28
|
+
return Object.keys(v).sort().join(',')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//getHello, 直接由驅動取拓樸資訊, 用於確認本容器確為replica set
|
|
32
|
+
let getHello = async () => {
|
|
33
|
+
let client = new mongodb.MongoClient(genUrlRs(ctPort))
|
|
34
|
+
let hello = null
|
|
35
|
+
try {
|
|
36
|
+
hello = await client.db(ctDb).admin().command({ hello: 1 })
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
await client.close()
|
|
40
|
+
}
|
|
41
|
+
return hello
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
before(async function() {
|
|
46
|
+
this.timeout(600000) //含拉取映像、服務啟動與選舉
|
|
47
|
+
ctPort = await startContainerRs(ctName)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
after(async function() {
|
|
51
|
+
this.timeout(120000)
|
|
52
|
+
await stopContainer(ctName)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
describe('insertBulk by transaction', function() {
|
|
57
|
+
let rt = null
|
|
58
|
+
let vans = {}
|
|
59
|
+
let vget = {}
|
|
60
|
+
|
|
61
|
+
before(async function() {
|
|
62
|
+
this.timeout(300000)
|
|
63
|
+
|
|
64
|
+
//確認本容器確為replica set, 否則以下即非在測交易路徑
|
|
65
|
+
//套件內判定交易可用與否之依據即為hello之setName與msg, 故此處成立即表示套件必走交易路徑
|
|
66
|
+
let hello = await getHello()
|
|
67
|
+
vget[1] = {
|
|
68
|
+
isRs: typeof hello.setName === 'string' && hello.setName.length > 0,
|
|
69
|
+
isWritablePrimary: hello.isWritablePrimary === true,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let cl = 'bulktx'
|
|
73
|
+
let wo = WOrm(genOpt(cl))
|
|
74
|
+
await wo.delAll()
|
|
75
|
+
|
|
76
|
+
//全新3筆, nInserted須等於n
|
|
77
|
+
vget[2] = await wo.insertBulk([
|
|
78
|
+
{ id: 't1', name: 'peter' },
|
|
79
|
+
{ id: 't2', name: 'rosemary' },
|
|
80
|
+
{ id: 't3', name: 'kettle' },
|
|
81
|
+
])
|
|
82
|
+
vget[3] = (await wo.select()).length
|
|
83
|
+
|
|
84
|
+
//撞既有主鍵須整批reject且回滾
|
|
85
|
+
rt = null
|
|
86
|
+
let nBefore = (await wo.select()).length
|
|
87
|
+
await wo.insertBulk([
|
|
88
|
+
{ id: 't4', name: 'new1' },
|
|
89
|
+
{ id: 't2', name: 'dup' },
|
|
90
|
+
{ id: 't5', name: 'new2' },
|
|
91
|
+
])
|
|
92
|
+
.then(function(msg) {
|
|
93
|
+
rt = `不應resolve: ${JSON.stringify(msg)}`
|
|
94
|
+
})
|
|
95
|
+
.catch(function(msg) {
|
|
96
|
+
rt = msg.toString()
|
|
97
|
+
})
|
|
98
|
+
vget[4] = rt.indexOf('E11000') >= 0 || rt.indexOf('duplicate key') >= 0
|
|
99
|
+
|
|
100
|
+
//回滾後資料表不得有任何新增
|
|
101
|
+
vget[5] = (await wo.select()).length - nBefore
|
|
102
|
+
vget[6] = [
|
|
103
|
+
await wo.selectByPk('t4'),
|
|
104
|
+
await wo.selectByPk('t5'),
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
//撞既有主鍵者本身不得被改動
|
|
108
|
+
vget[7] = (await wo.selectByPk('t2')).name
|
|
109
|
+
|
|
110
|
+
//同批含重複主鍵須整批reject且回滾
|
|
111
|
+
rt = null
|
|
112
|
+
nBefore = (await wo.select()).length
|
|
113
|
+
await wo.insertBulk([
|
|
114
|
+
{ id: 't6', name: 'a' },
|
|
115
|
+
{ id: 't6', name: 'b' },
|
|
116
|
+
{ id: 't7', name: 'c' },
|
|
117
|
+
])
|
|
118
|
+
.then(function(msg) {
|
|
119
|
+
rt = `不應resolve: ${JSON.stringify(msg)}`
|
|
120
|
+
})
|
|
121
|
+
.catch(function(msg) {
|
|
122
|
+
rt = msg.toString()
|
|
123
|
+
})
|
|
124
|
+
vget[8] = rt.indexOf('E11000') >= 0 || rt.indexOf('duplicate key') >= 0
|
|
125
|
+
vget[9] = (await wo.select()).length - nBefore
|
|
126
|
+
vget[10] = [
|
|
127
|
+
await wo.selectByPk('t6'),
|
|
128
|
+
await wo.selectByPk('t7'),
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
//較大批次亦須全有全無, 用於驗證分批送出時之回滾
|
|
132
|
+
rt = null
|
|
133
|
+
await wo.delAll()
|
|
134
|
+
let rs = Array.from({ length: 200 }, (v, k) => {
|
|
135
|
+
return { id: `m${k}`, name: `n${k}` }
|
|
136
|
+
})
|
|
137
|
+
vget[11] = await wo.insertBulk(rs)
|
|
138
|
+
vget[12] = (await wo.select()).length
|
|
139
|
+
|
|
140
|
+
//末筆撞既有主鍵, 前199筆皆不得殘留
|
|
141
|
+
rt = null
|
|
142
|
+
await wo.delAll()
|
|
143
|
+
await wo.insert({ id: 'm199', name: 'pre' })
|
|
144
|
+
nBefore = (await wo.select()).length
|
|
145
|
+
await wo.insertBulk(rs)
|
|
146
|
+
.then(function(msg) {
|
|
147
|
+
rt = `不應resolve: ${JSON.stringify(msg)}`
|
|
148
|
+
})
|
|
149
|
+
.catch(function(msg) {
|
|
150
|
+
rt = msg.toString()
|
|
151
|
+
})
|
|
152
|
+
vget[13] = rt.indexOf('E11000') >= 0 || rt.indexOf('duplicate key') >= 0
|
|
153
|
+
vget[14] = (await wo.select()).length - nBefore
|
|
154
|
+
|
|
155
|
+
//輸入無效與單一物件
|
|
156
|
+
let woS = WOrm(genOpt('bulktxmisc'))
|
|
157
|
+
await woS.delAll()
|
|
158
|
+
vget[15] = await woS.insertBulk(null)
|
|
159
|
+
vget[16] = await woS.insertBulk({ id: 's1', name: 'single' })
|
|
160
|
+
vget[17] = [genKeys(vget[15]), genKeys(vget[16])]
|
|
161
|
+
|
|
162
|
+
//事件
|
|
163
|
+
let woEv = WOrm(genOpt('bulktxevent'))
|
|
164
|
+
await woEv.delAll()
|
|
165
|
+
let evs = []
|
|
166
|
+
woEv.on('change', function(mode) {
|
|
167
|
+
evs.push(`change:${mode}`)
|
|
168
|
+
})
|
|
169
|
+
woEv.on('error', function(mode) {
|
|
170
|
+
evs.push(`error:${mode}`)
|
|
171
|
+
})
|
|
172
|
+
await woEv.insertBulk({ id: 'e1', name: 'a' })
|
|
173
|
+
await woEv.insertBulk({ id: 'e1', name: 'b' }).catch(() => {})
|
|
174
|
+
vget[18] = evs
|
|
175
|
+
|
|
176
|
+
//其餘函數於replica set下行為須與standalone相同
|
|
177
|
+
let woO = WOrm(genOpt('bulktxother'))
|
|
178
|
+
await woO.delAll()
|
|
179
|
+
vget[19] = await woO.insert([{ id: 'o1' }, { id: 'o1' }, { id: 'o2' }])
|
|
180
|
+
vget[20] = await woO.save({ id: 'o1', name: 'x' })
|
|
181
|
+
vget[21] = await woO.del({ id: 'o2' })
|
|
182
|
+
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
vans[1] = { isRs: true, isWritablePrimary: true }
|
|
186
|
+
it(`should get ${JSON.stringify(vans[1])} for topology being replica set`, async function() {
|
|
187
|
+
assert.strict.deepStrictEqual(vget[1], vans[1])
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
vans[2] = { n: 3, nInserted: 3, ok: 1 }
|
|
191
|
+
it(`should get ${JSON.stringify(vans[2])} for insertBulk 3 new records`, async function() {
|
|
192
|
+
assert.strict.deepStrictEqual(vget[2], vans[2])
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
vans[3] = 3
|
|
196
|
+
it(`should get ${JSON.stringify(vans[3])} for records after insertBulk`, async function() {
|
|
197
|
+
assert.strict.deepStrictEqual(vget[3], vans[3])
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
vans[4] = true
|
|
201
|
+
it(`should get ${JSON.stringify(vans[4])} for reject of insertBulk with existed id`, async function() {
|
|
202
|
+
assert.strict.deepStrictEqual(vget[4], vans[4])
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
vans[5] = 0
|
|
206
|
+
it(`should get ${JSON.stringify(vans[5])} for rollback after rejected insertBulk`, async function() {
|
|
207
|
+
assert.strict.deepStrictEqual(vget[5], vans[5])
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
vans[6] = [null, null]
|
|
211
|
+
it(`should get ${JSON.stringify(vans[6])} for not writing any record in rejected insertBulk`, async function() {
|
|
212
|
+
assert.strict.deepStrictEqual(vget[6], vans[6])
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
vans[7] = 'rosemary'
|
|
216
|
+
it(`should get ${JSON.stringify(vans[7])} for not modifying existed record by rejected insertBulk`, async function() {
|
|
217
|
+
assert.strict.deepStrictEqual(vget[7], vans[7])
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
vans[8] = true
|
|
221
|
+
it(`should get ${JSON.stringify(vans[8])} for reject of insertBulk with duplicated id in same batch`, async function() {
|
|
222
|
+
assert.strict.deepStrictEqual(vget[8], vans[8])
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
vans[9] = 0
|
|
226
|
+
it(`should get ${JSON.stringify(vans[9])} for rollback after insertBulk with duplicated id`, async function() {
|
|
227
|
+
assert.strict.deepStrictEqual(vget[9], vans[9])
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
vans[10] = [null, null]
|
|
231
|
+
it(`should get ${JSON.stringify(vans[10])} for not writing any record by duplicated id in same batch`, async function() {
|
|
232
|
+
assert.strict.deepStrictEqual(vget[10], vans[10])
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
vans[11] = { n: 200, nInserted: 200, ok: 1 }
|
|
236
|
+
it(`should get ${JSON.stringify(vans[11])} for insertBulk 200 records`, async function() {
|
|
237
|
+
assert.strict.deepStrictEqual(vget[11], vans[11])
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
vans[12] = 200
|
|
241
|
+
it(`should get ${JSON.stringify(vans[12])} for records after insertBulk 200 records`, async function() {
|
|
242
|
+
assert.strict.deepStrictEqual(vget[12], vans[12])
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
vans[13] = true
|
|
246
|
+
it(`should get ${JSON.stringify(vans[13])} for reject of insertBulk 200 records with last one conflicted`, async function() {
|
|
247
|
+
assert.strict.deepStrictEqual(vget[13], vans[13])
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
vans[14] = 0
|
|
251
|
+
it(`should get ${JSON.stringify(vans[14])} for rollback of 200 records batch`, async function() {
|
|
252
|
+
assert.strict.deepStrictEqual(vget[14], vans[14])
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
vans[15] = { n: 0, nInserted: 0, ok: 1 }
|
|
256
|
+
it(`should get ${JSON.stringify(vans[15])} for insertBulk with invalid data`, async function() {
|
|
257
|
+
assert.strict.deepStrictEqual(vget[15], vans[15])
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
vans[16] = { n: 1, nInserted: 1, ok: 1 }
|
|
261
|
+
it(`should get ${JSON.stringify(vans[16])} for insertBulk with single object`, async function() {
|
|
262
|
+
assert.strict.deepStrictEqual(vget[16], vans[16])
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
vans[17] = ['n,nInserted,ok', 'n,nInserted,ok']
|
|
266
|
+
it(`should get ${JSON.stringify(vans[17])} for fixed key set of insertBulk`, async function() {
|
|
267
|
+
assert.strict.deepStrictEqual(vget[17], vans[17])
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
vans[18] = ['change:insertBulk', 'error:insertBulk']
|
|
271
|
+
it(`should get ${JSON.stringify(vans[18])} for events of insertBulk`, async function() {
|
|
272
|
+
assert.strict.deepStrictEqual(vget[18], vans[18])
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
vans[19] = { n: 3, nInserted: 2, ok: 1 }
|
|
276
|
+
it(`should get ${JSON.stringify(vans[19])} for insert on replica set`, async function() {
|
|
277
|
+
assert.strict.deepStrictEqual(vget[19], vans[19])
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
vans[20] = [{ n: 1, nInserted: 0, nModified: 1, ok: 1 }]
|
|
281
|
+
it(`should get ${JSON.stringify(vans[20])} for save on replica set`, async function() {
|
|
282
|
+
assert.strict.deepStrictEqual(vget[20], vans[20])
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
vans[21] = [{ n: 1, nDeleted: 1, ok: 1 }]
|
|
286
|
+
it(`should get ${JSON.stringify(vans[21])} for del on replica set`, async function() {
|
|
287
|
+
assert.strict.deepStrictEqual(vget[21], vans[21])
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
})
|
package/test/lib/api-setup.mjs
CHANGED
|
@@ -45,6 +45,13 @@ let genUrl = (port) => {
|
|
|
45
45
|
return `mongodb://${ctUser}:${ctPassword}@127.0.0.1:${port}`
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
//genUrlRs, replica set容器之連線字串
|
|
49
|
+
//directConnection=true係因容器內之replica set成員位址為127.0.0.1:27017, 由宿主機依該位址連不到,
|
|
50
|
+
//故不可令驅動走成員探索, 須直連本次映射之對外埠
|
|
51
|
+
let genUrlRs = (port) => {
|
|
52
|
+
return `mongodb://127.0.0.1:${port}/?directConnection=true`
|
|
53
|
+
}
|
|
54
|
+
|
|
48
55
|
//probeReady, 以mongodb驅動直連容器對外埠試跑ping
|
|
49
56
|
//不使用容器內mongosh, 因mongo映像初始化期間會先啟動僅監聽unix socket之臨時服務,
|
|
50
57
|
//容器內探測會提早回報就緒, 直連TCP方為測試實際所走路徑
|
|
@@ -64,10 +71,28 @@ let probeReady = async (port) => {
|
|
|
64
71
|
return ok
|
|
65
72
|
}
|
|
66
73
|
|
|
67
|
-
//
|
|
74
|
+
//probeReadyRs, replica set容器須等到本節點成為PRIMARY方可寫入
|
|
75
|
+
let probeReadyRs = async (port) => {
|
|
76
|
+
let client = new mongodb.MongoClient(genUrlRs(port), { serverSelectionTimeoutMS: 2000 })
|
|
77
|
+
let ok = false
|
|
78
|
+
try {
|
|
79
|
+
let hello = await client.db('admin').command({ hello: 1 })
|
|
80
|
+
ok = hello.isWritablePrimary === true
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
ok = false
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
await client.close().catch(() => {})
|
|
87
|
+
}
|
|
88
|
+
return ok
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
//runContainer, 創建容器並回傳對外埠
|
|
68
92
|
//對外埠由getFreePort動態取得, 避免與既有MongoDB服務搶27017, 亦避免多測試檔並行時互搶同一埠
|
|
69
|
-
//
|
|
70
|
-
|
|
93
|
+
//getFreePort取得之埠於關閉監聽至docker實際綁定之間可能已被他者佔用, 多測試檔並行時尤其容易發生,
|
|
94
|
+
//故綁定失敗即改取新埠重試
|
|
95
|
+
let runContainer = async (ctName, genArgs) => {
|
|
71
96
|
|
|
72
97
|
//check docker
|
|
73
98
|
await docker(['version', '--format', '{{.Server.Version}}'])
|
|
@@ -78,8 +103,7 @@ let startContainer = async (ctName) => {
|
|
|
78
103
|
//清除前次殘留容器
|
|
79
104
|
await docker(['rm', '-f', ctName]).catch(() => {})
|
|
80
105
|
|
|
81
|
-
//run
|
|
82
|
-
//多測試檔並行時尤其容易發生, 故綁定失敗即改取新埠重試
|
|
106
|
+
//run
|
|
83
107
|
let ctPort = null
|
|
84
108
|
let nTry = 10
|
|
85
109
|
for (let i = 0; i < nTry; i++) {
|
|
@@ -89,7 +113,7 @@ let startContainer = async (ctName) => {
|
|
|
89
113
|
|
|
90
114
|
//run
|
|
91
115
|
let err = null
|
|
92
|
-
await docker(
|
|
116
|
+
await docker(genArgs(ctPort))
|
|
93
117
|
.catch((e) => {
|
|
94
118
|
err = e
|
|
95
119
|
})
|
|
@@ -111,20 +135,75 @@ let startContainer = async (ctName) => {
|
|
|
111
135
|
|
|
112
136
|
}
|
|
113
137
|
|
|
114
|
-
|
|
115
|
-
|
|
138
|
+
return ctPort
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
//waitReady, 輪詢至就緒, 首次需拉取映像故給予較長時間
|
|
142
|
+
let waitReady = async (fnProbe, port, msgErr) => {
|
|
116
143
|
for (let i = 0; i < 90; i++) {
|
|
117
|
-
let ok = await
|
|
144
|
+
let ok = await fnProbe(port)
|
|
118
145
|
if (ok) {
|
|
119
|
-
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
await delay(2000)
|
|
149
|
+
}
|
|
150
|
+
throw new Error(msgErr)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
//startContainer, 創建standalone容器並等待服務就緒, 回傳容器對外埠
|
|
154
|
+
//mongo映像以MONGO_INITDB_ROOT_USERNAME、MONGO_INITDB_ROOT_PASSWORD即建妥帳號, 無須另跑建置語句
|
|
155
|
+
let startContainer = async (ctName) => {
|
|
156
|
+
|
|
157
|
+
//run
|
|
158
|
+
let ctPort = await runContainer(ctName, (port) => {
|
|
159
|
+
return ['run', '-d', '--rm', '--name', ctName, '-e', `MONGO_INITDB_ROOT_USERNAME=${ctUser}`, '-e', `MONGO_INITDB_ROOT_PASSWORD=${ctPassword}`, '-p', `${port}:27017`, ctImage]
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
//waitReady
|
|
163
|
+
await waitReady(probeReady, ctPort, 'mongodb容器啟動逾時')
|
|
164
|
+
|
|
165
|
+
return ctPort
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
//startContainerRs, 創建replica set容器並等待PRIMARY就緒, 回傳容器對外埠
|
|
169
|
+
//交易須replica set或分片叢集方可用, standalone不支援, 故驗證交易路徑須另起本容器
|
|
170
|
+
//本容器不開啟認證: --replSet配合MONGO_INITDB_ROOT_*須另備keyFile以供成員間內部認證, 於測試無必要
|
|
171
|
+
let startContainerRs = async (ctName) => {
|
|
172
|
+
|
|
173
|
+
//run
|
|
174
|
+
let ctPort = await runContainer(ctName, (port) => {
|
|
175
|
+
return ['run', '-d', '--rm', '--name', ctName, '-p', `${port}:27017`, ctImage, '--replSet', 'rs0', '--bind_ip_all']
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
//等待服務可回應, 此時尚未initiate故非PRIMARY
|
|
179
|
+
let up = false
|
|
180
|
+
for (let i = 0; i < 90; i++) {
|
|
181
|
+
let ok = await docker(['exec', ctName, 'mongosh', '--quiet', '--eval', 'db.adminCommand({ping:1}).ok'])
|
|
182
|
+
.then(() => true)
|
|
183
|
+
.catch(() => false)
|
|
184
|
+
if (ok) {
|
|
185
|
+
up = true
|
|
120
186
|
break
|
|
121
187
|
}
|
|
122
188
|
await delay(2000)
|
|
123
189
|
}
|
|
124
|
-
if (!
|
|
125
|
-
throw new Error('mongodb容器啟動逾時')
|
|
190
|
+
if (!up) {
|
|
191
|
+
throw new Error('mongodb replica set容器啟動逾時')
|
|
126
192
|
}
|
|
127
193
|
|
|
194
|
+
//initiate, 單成員之replica set即足以啟用交易
|
|
195
|
+
await docker(['exec', ctName, 'mongosh', '--quiet', '--eval', 'rs.initiate({_id:"rs0",members:[{_id:0,host:"127.0.0.1:27017"}]})'])
|
|
196
|
+
.catch((err) => {
|
|
197
|
+
//已initiate者再次呼叫會報AlreadyInitialized, 屬正常
|
|
198
|
+
let msg = `${err.stderr || ''}${err.message || ''}`
|
|
199
|
+
if (msg.indexOf('AlreadyInitialized') < 0) {
|
|
200
|
+
throw err
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
//waitReady, 等待本節點成為PRIMARY
|
|
205
|
+
await waitReady(probeReadyRs, ctPort, 'mongodb replica set選舉逾時')
|
|
206
|
+
|
|
128
207
|
return ctPort
|
|
129
208
|
}
|
|
130
209
|
|
|
@@ -137,6 +216,8 @@ let stopContainer = async (ctName) => {
|
|
|
137
216
|
export {
|
|
138
217
|
delay,
|
|
139
218
|
genUrl,
|
|
219
|
+
genUrlRs,
|
|
140
220
|
startContainer,
|
|
221
|
+
startContainerRs,
|
|
141
222
|
stopContainer
|
|
142
223
|
}
|