@cloudbase/node-sdk
Version:
tencent cloud base server sdk for node.js
654 lines (578 loc) • 22 kB
text/typescript
import * as assert from 'power-assert'
import tcb from '../../../src/index'
import * as Config from '../../config.local'
import * as common from '../../common/index'
const app = tcb.init(Config)
const db = app.database()
const _ = db.command
const offset = 60 * 1000
describe('transaction', async () => {
let collection = null
const collectionName = 'test-passages'
const date = new Date()
const data = [
{
_id: '1',
category: 'Web',
tags: ['JavaScript', 'C#'],
date,
geo: new db.Geo.Point(90, 23),
num: 2
},
{ _id: '2', category: 'Web', tags: ['Go', 'C#'] },
{ _id: '3', category: 'Life', tags: ['Go', 'Python', 'JavaScript'] },
{ _id: '4', serverDate1: db.serverDate(), serverDate2: db.serverDate({ offset }) }
]
beforeEach(async () => {
collection = await common.safeCollection(db, collectionName)
// 测试环境不稳定, 清除之前的影响
await collection.remove()
const success = await collection.create(data)
assert.strictEqual(success, true)
})
afterEach(async () => {
const success = await collection.remove()
assert.strictEqual(success, true)
})
it('发起事务', async () => {
const transaction = await db.startTransaction()
assert.strictEqual(typeof transaction._id, 'string')
})
it('提交事务', async () => {
const transaction = await db.startTransaction()
const res = await transaction.commit()
assert.strictEqual(typeof res.requestId, 'string')
})
it('runTransaction', async () => {
await db.runTransaction(async function(transaction) {
// 事务内插入 含date geo类型文档
const insertDoc = { _id: 'lluke', date: date, geo: new db.Geo.Point(90, 23) }
const insertRes = await transaction.collection(collectionName).add(insertDoc)
console.log('insertRes:', insertRes)
assert(insertRes.inserted === 1)
const doc = await transaction
.collection(collectionName)
.doc('lluke')
.get()
console.log('doc:', doc)
assert(doc.data, insertDoc)
// 事务外插入含date geo serverDate 类型文档
const doc1 = await transaction
.collection(collectionName)
.doc('1')
.get()
const doc4 = await transaction
.collection(collectionName)
.doc('4')
.get()
assert.deepStrictEqual(doc1.data, data[0])
assert.deepStrictEqual(
doc4.data.serverDate1.getTime() + offset === doc4.data.serverDate2.getTime(),
true
)
// 事务内插入 含 serverDate 类型文档 报错不支持
// const insertRes = await transaction.collection(collectionName).add({_id: 'lluke', serverDate1: db.serverDate(), serverDate2: db.serverDate({ offset })})
// console.log('insertRes:', insertRes)
// const lukeData = await transaction.collection(collectionName).doc('lluke').get()
// assert(lukeData.data.serverDate1.getTime() + offset === lukeData.data.serverDate2.getTime(), true)
})
})
it('事务内更新含特殊类型 字段文档', async () => {
await db.runTransaction(async function(transaction) {
const newDate = new Date()
const newGeo = new db.Geo.Point(90, 23)
const updateRes = await transaction
.collection(collectionName)
.doc('1')
.update({
num: _.inc(1),
date: newDate,
geo: newGeo
})
assert(updateRes.updated === 1)
const res = await transaction
.collection(collectionName)
.doc('1')
.get()
assert(res.data.num === 3, true)
assert(res.data.date, newDate)
assert(res.data.geo, newGeo)
})
})
it('insert', async () => {
const transaction = await db.startTransaction()
const res = await transaction
.collection(collectionName)
.add({ category: 'Web', tags: ['JavaScript', 'C#'], date })
assert(res.id !== undefined && res.inserted === 1)
const result = await transaction.commit()
assert.strictEqual(typeof result.requestId, 'string')
})
it('insert with custom docid', async () => {
const docId = +new Date()
const transaction = await db.startTransaction()
const res = await transaction
.collection(collectionName)
.add({ _id: docId, category: 'Web', tags: ['JavaScript', 'C#'], date })
assert(res.id == docId && res.inserted === 1)
const result = await transaction.commit()
assert.strictEqual(typeof result.requestId, 'string')
})
it('get', async () => {
// const docRef = db.collection(collectionName).doc('1')
const transaction = await db.startTransaction()
// const doc = await transaction.get(docRef)
const doc = await transaction
.collection(collectionName)
.doc('1')
.get()
assert.deepStrictEqual(doc.data, data[0])
const res = await transaction.commit()
assert(res.requestId)
})
it('get不存在的文档,返回null', async () => {
// const docRef = db.collection(collectionName).doc('114514')
const transaction = await db.startTransaction()
// const doc = await transaction.get(docRef)
const doc = await transaction
.collection(collectionName)
.doc('114514')
.get()
assert.strictEqual(doc.data, null)
const res = await transaction.commit()
assert(res.requestId)
})
it('事务回滚', async () => {
const transaction = await db.startTransaction()
// const docRef = db.collection(collectionName).doc('1')
// const doc = await transaction.get(docRef)
const doc = await transaction
.collection(collectionName)
.doc('1')
.get()
assert.deepStrictEqual(doc.data, data[0])
const res = await transaction.rollback()
assert(res.requestId)
})
it('update', async () => {
// const docRef = db.collection(collectionName).doc('1')
const transaction = await db.startTransaction()
// let doc = await transaction.get(docRef)
let doc = await transaction
.collection(collectionName)
.doc('1')
.get()
assert.deepStrictEqual(doc.data, data[0])
const date = new Date()
// const updateResult = await transaction.update(docRef, {
// category: 'Node.js',
// date
// })
const updateResult = await transaction
.collection(collectionName)
.doc('1')
.update({
category: 'Node.js',
date
})
assert.strictEqual(updateResult.updated, 1)
// doc = await transaction.get(docRef)
doc = await transaction
.collection(collectionName)
.doc('1')
.get()
assert.deepStrictEqual(doc.data, {
...data[0],
date,
category: 'Node.js'
})
const res = await transaction.commit()
assert(res.requestId)
})
it('set doc', async () => {
// const docRef = db.collection(collectionName).doc('1')
const transaction = await db.startTransaction()
// let doc = await transaction.get(docRef)
let doc = await transaction
.collection(collectionName)
.doc('1')
.get()
const date = new Date()
// const updateResult = await transaction.set(docRef, {
// ...data[0],
// date,
// category: 'Node.js'
// })
const newData = { ...data[0] }
delete newData['_id']
const updateResult = await transaction
.collection(collectionName)
.doc('1')
.set({
...newData,
date,
category: 'Node.js'
})
assert.strictEqual(updateResult.updated, 1)
// doc = await transaction.get(docRef)
doc = await transaction
.collection(collectionName)
.doc('1')
.get()
assert.deepStrictEqual(doc.data, {
...data[0],
date,
category: 'Node.js'
})
const res = await transaction.commit()
assert(res.requestId)
})
it('upsert doc', async () => {
// const docRef = db.collection(collectionName).doc('114514')
const transaction = await db.startTransaction()
// let doc = await transaction.get(docRef)
let doc = await transaction
.collection(collectionName)
.doc('114514')
.get()
assert.deepStrictEqual(doc.data, null)
const date = new Date()
const data = {
category: 'Node.js',
date
}
// const updateResult = await transaction.set(docRef, data)
const updateResult = await transaction
.collection(collectionName)
.doc('114514')
.set(data)
assert.strictEqual(updateResult.upserted.length, 1)
// doc = await transaction.get(docRef)
doc = await transaction
.collection(collectionName)
.doc('114514')
.get()
assert.deepStrictEqual(doc.data, {
_id: '114514',
...data
})
const res = await transaction.rollback()
assert(res.requestId)
})
it('delete doc', async () => {
// 前面测试用例更改过 _id = 1 的数据
// const docRef = db.collection(collectionName).doc('2')
const transaction = await db.startTransaction()
// let doc = await transaction.get(docRef)
let doc = await transaction
.collection(collectionName)
.doc('2')
.get()
assert.deepStrictEqual(doc.data, data[1])
// const deleteResult = await transaction.delete(docRef)
const deleteResult = await transaction
.collection(collectionName)
.doc('2')
.delete()
assert.strictEqual(deleteResult.deleted, 1)
// doc = await transaction.get(docRef)
doc = await transaction
.collection(collectionName)
.doc('2')
.get()
assert.deepStrictEqual(doc.data, null)
await transaction.commit()
})
it('runTransaction with customResult', async () => {
// 验证自定义成功返回
const result = await db.runTransaction(async function(transaction) {
const doc = await transaction
.collection(collectionName)
.doc('1')
.get()
assert.deepStrictEqual(doc.data, data[0])
// assert(doc.data)
return 'luke'
})
assert(result === 'luke')
})
// runTransaction rollback
it('rollback within runTransaction', async () => {
try {
await db.runTransaction(async function(transaction) {
const doc = await transaction
.collection(collectionName)
.doc('1')
.get()
assert.deepStrictEqual(doc.data, data[0])
await transaction.rollback('luke')
})
} catch (err) {
assert(err === 'luke')
}
try {
await db.runTransaction(async function(transaction) {
const doc = await transaction
.collection(collectionName)
.doc('1')
.get()
assert.deepStrictEqual(doc.data, data[0])
await transaction.rollback()
})
} catch (err) {
assert(err === undefined)
}
try {
await db.runTransaction(async transaction => {
const doc = await transaction
.collection(collectionName)
.doc('1')
.get()
assert.deepStrictEqual(doc.data, data[0])
// mock 事务冲突
throw {
code: 'DATABASE_TRANSACTION_CONFLICT',
message:
'[ResourceUnavailable.TransactionConflict] Transaction is conflict, maybe resource operated by others. Please check your request, but if the problem persists, contact us.'
}
})
} catch (e) {
assert(e.code === 'DATABASE_TRANSACTION_CONFLICT')
}
try {
const docRef1 = db.collection(collectionName).doc('1')
await db.runTransaction(async transaction => {
const doc = await transaction
.collection(collectionName)
.doc('1')
.get()
await docRef1.set({
category: 'wwwwwwwwwwwwwwwww'
})
const res = await transaction
.collection(collectionName)
.doc('1')
.update({
category: 'transactiontransactiontransaction'
})
})
} catch (e) {
// 因为mongo write conflict时会自动rollback,兜底的rollback会报错 非conflict错误
assert(e.code)
// assert(e.code === 'DATABASE_TRANSACTION_CONFLICT')
}
})
it('delete doc and abort', async () => {
// 前面测试用例删除了 _id = 2 的数据
const docRef = db.collection(collectionName).doc('3')
const transaction = await db.startTransaction()
// let doc = await transaction.get(docRef)
let doc = await transaction
.collection(collectionName)
.doc('3')
.get()
// const deleteResult = await transaction.delete(docRef)
const deleteResult = await transaction
.collection(collectionName)
.doc('3')
.delete()
assert.strictEqual(deleteResult.deleted, 1)
// doc = await transaction.get(docRef)
doc = await transaction
.collection(collectionName)
.doc('3')
.get()
assert.deepStrictEqual(doc.data, null)
await transaction.rollback()
const res = await docRef.get()
// const res = await transaction.collection(collectionName).doc('3').get()
assert.deepStrictEqual(res.data[0], data[2])
})
it('事务提交后, 不能进行其它操作', async () => {
// const docRef = db.collection(collectionName).doc('1')
const transaction = await db.startTransaction()
await transaction.commit()
await assert.rejects(
async () => {
// await transaction.set(docRef, {
// category: 'Node.js'
// })
await transaction
.collection(collectionName)
.doc('1')
.set({
category: 'Node.js'
})
},
{
code: 'DATABASE_TRANSACTION_FAIL',
message:
'[ResourceUnavailable.TransactionNotExist] Transaction does not exist on the server, transaction must be commit or abort in 30 seconds. Please check your request, but if the problem persists, contact us.'
}
)
})
it('冲突检测', async () => {
// const docRef = db.collection(collectionName).doc('1')
const transaction1 = await db.startTransaction(),
transaction2 = await db.startTransaction()
// 事务1先读取数据
// const doc = await transaction1.get(docRef)
const doc = await transaction1
.collection(collectionName)
.doc('1')
.get()
// 事务2更改之前事务1读取的数据,并且提交
// await transaction2.update(docRef, {
// category: '冲突检测'
// })
await transaction2
.collection(collectionName)
.doc('1')
.update({
category: '冲突检测'
})
// 由于事务1读取的数据没有时效性,故报错
await assert.rejects(
async () => {
// await transaction1.update(docRef, {
// category: doc.data.category + '冲突检测'
// })
await transaction1
.collection(collectionName)
.doc('1')
.update({
category: doc.data.category + '冲突检测'
})
},
{
code: 'DATABASE_TRANSACTION_CONFLICT'
}
)
await transaction2.commit()
})
it('读快照', async () => {
const docRef = db.collection(collectionName).doc('1')
// 启动事务
const transaction = await db.startTransaction()
// 修改数据
// await transaction.update(docRef, {
// category: 'update in transaction'
// })
await transaction
.collection(collectionName)
.doc('1')
.update({
category: 'update in transaction'
})
const result = await docRef.get()
// 事务读,读的是开始时刻的快照
// const doc_new = await transaction.get(docRef)
const doc_new = await transaction
.collection(collectionName)
.doc('1')
.get()
assert.deepStrictEqual(result.data[0], data[0])
assert.deepStrictEqual(doc_new.data, {
...data[0],
category: 'update in transaction'
})
await transaction.rollback()
})
it('读偏', async () => {
const docRef1 = db.collection(collectionName).doc('1')
const docRef2 = db.collection(collectionName).doc('2')
// 启动事务
const transaction = await db.startTransaction()
// 修改数据
// console.log(await transaction.get(docRef1))
console.log(
await transaction
.collection(collectionName)
.doc('1')
.get()
)
await docRef1.set({
category: 'wwwwwwwwwwwwwwwww'
})
await docRef2.set({
category: 'hhhhhhhhhhhhhhh'
})
// 事务读,读的是开始时刻的快照
// const snapshot1 = await transaction.get(docRef1)
// const snapshot2 = await transaction.get(docRef2)
const snapshot1 = await transaction
.collection(collectionName)
.doc('1')
.get()
const snapshot2 = await transaction
.collection(collectionName)
.doc('2')
.get()
assert.deepStrictEqual(snapshot1.data, data[0])
assert.deepStrictEqual(snapshot2.data, data[1])
// 外部已经修改了数据,事务内修改应该失败
await assert.rejects(
async () => {
// await transaction.update(docRef1, {
// category: 'transactiontransactiontransaction'
// })
await transaction
.collection(collectionName)
.doc('1')
.update({
category: 'transactiontransactiontransaction'
})
},
{
code: 'DATABASE_TRANSACTION_CONFLICT',
message:
'[ResourceUnavailable.TransactionConflict] Transaction is conflict, maybe resource operated by others. Please check your request, but if the problem persists, contact us.'
}
)
})
it('write skew', async () => {
const docRef1 = db.collection(collectionName).doc('1')
const docRef2 = db.collection(collectionName).doc('2')
const transaction1 = await db.startTransaction()
const transaction2 = await db.startTransaction()
// 事务1:读1写2
// 事务2:读2写1
// const doc1 = await transaction1.get(docRef1)
// const doc2 = await transaction2.get(docRef2)
const doc1 = await transaction1
.collection(collectionName)
.doc('1')
.get()
const doc2 = await transaction2
.collection(collectionName)
.doc('2')
.get()
// await transaction1.set(docRef2, {
// category: doc1.data.category + 'doc1'
// })
// await transaction2.set(docRef1, {
// category: doc2.data.category + 'doc2'
// })
await transaction1
.collection(collectionName)
.doc('2')
.set({
category: doc1.data.category + 'doc1'
})
await transaction2
.collection(collectionName)
.doc('1')
.set({
category: doc2.data.category + 'doc2'
})
// 由于事务2读取的数据没有时效性,故报错
try {
await transaction1.commit()
await transaction2.commit()
} catch (error) {
console.log(error)
}
console.log(await docRef1.get())
console.log(await docRef2.get())
})
})