go.temporal.io/server@v1.23.0/common/persistence/sql/sqlplugin/tests/history_node.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package tests 26 27 import ( 28 "math" 29 "math/rand" 30 "sort" 31 "testing" 32 33 "github.com/stretchr/testify/require" 34 "github.com/stretchr/testify/suite" 35 36 "go.temporal.io/server/common/persistence/sql" 37 "go.temporal.io/server/common/persistence/sql/sqlplugin" 38 "go.temporal.io/server/common/primitives" 39 "go.temporal.io/server/common/shuffle" 40 ) 41 42 type ( 43 historyNodeSuite struct { 44 suite.Suite 45 *require.Assertions 46 47 store sqlplugin.HistoryNode 48 } 49 ) 50 51 const ( 52 testHistoryNodeEncoding = "random encoding" 53 ) 54 55 var ( 56 testHistoryNodeData = []byte("random history node data") 57 ) 58 59 func NewHistoryNodeSuite( 60 t *testing.T, 61 store sqlplugin.HistoryNode, 62 ) *historyNodeSuite { 63 return &historyNodeSuite{ 64 Assertions: require.New(t), 65 store: store, 66 } 67 } 68 69 func (s *historyNodeSuite) SetupSuite() { 70 71 } 72 73 func (s *historyNodeSuite) TearDownSuite() { 74 75 } 76 77 func (s *historyNodeSuite) SetupTest() { 78 s.Assertions = require.New(s.T()) 79 } 80 81 func (s *historyNodeSuite) TearDownTest() { 82 83 } 84 85 func (s *historyNodeSuite) TestInsert_Success() { 86 shardID := rand.Int31() 87 treeID := primitives.NewUUID() 88 branchID := primitives.NewUUID() 89 nodeID := rand.Int63() 90 prevTransactionID := rand.Int63() 91 transactionID := rand.Int63() 92 93 node := s.newRandomNodeRow(shardID, treeID, branchID, nodeID, prevTransactionID, transactionID) 94 result, err := s.store.InsertIntoHistoryNode(newExecutionContext(), &node) 95 s.NoError(err) 96 rowsAffected, err := result.RowsAffected() 97 s.NoError(err) 98 s.Equal(1, int(rowsAffected)) 99 } 100 101 func (s *historyNodeSuite) TestInsert_Fail_Duplicate() { 102 shardID := rand.Int31() 103 treeID := primitives.NewUUID() 104 branchID := primitives.NewUUID() 105 nodeID := rand.Int63() 106 prevTransactionID := rand.Int63() 107 transactionID := rand.Int63() 108 109 node := s.newRandomNodeRow(shardID, treeID, branchID, nodeID, prevTransactionID, transactionID) 110 result, err := s.store.InsertIntoHistoryNode(newExecutionContext(), &node) 111 s.NoError(err) 112 rowsAffected, err := result.RowsAffected() 113 s.NoError(err) 114 s.Equal(1, int(rowsAffected)) 115 116 node = s.newRandomNodeRow(shardID, treeID, branchID, nodeID, prevTransactionID, transactionID) 117 _, err = s.store.InsertIntoHistoryNode(newExecutionContext(), &node) 118 s.NoError(err) // TODO persistence layer should do proper error translation 119 } 120 121 func (s *historyNodeSuite) TestInsertSelect_Single() { 122 pageSize := 100 123 124 shardID := rand.Int31() 125 treeID := primitives.NewUUID() 126 branchID := primitives.NewUUID() 127 nodeID := int64(1) 128 prevTransactionID := rand.Int63() 129 transactionID := rand.Int63() 130 131 node := s.newRandomNodeRow(shardID, treeID, branchID, nodeID, prevTransactionID, transactionID) 132 result, err := s.store.InsertIntoHistoryNode(newExecutionContext(), &node) 133 s.NoError(err) 134 rowsAffected, err := result.RowsAffected() 135 s.NoError(err) 136 s.Equal(1, int(rowsAffected)) 137 138 selectFilter := sqlplugin.HistoryNodeSelectFilter{ 139 ShardID: shardID, 140 TreeID: treeID, 141 BranchID: branchID, 142 MinNodeID: nodeID, 143 MinTxnID: sql.MinTxnID, 144 MaxNodeID: math.MaxInt64, 145 PageSize: pageSize, 146 } 147 rows, err := s.store.RangeSelectFromHistoryNode(newExecutionContext(), selectFilter) 148 s.NoError(err) 149 // NOTE: TxnID is *= -1 within InsertIntoHistoryNode 150 node.TxnID = -node.TxnID 151 for index := range rows { 152 rows[index].ShardID = shardID 153 rows[index].TreeID = treeID 154 rows[index].BranchID = branchID 155 } 156 s.Equal([]sqlplugin.HistoryNodeRow{node}, rows) 157 } 158 159 func (s *historyNodeSuite) TestInsertSelect_Multiple() { 160 numNodeIDs := 100 161 nodePerNodeID := 2 + rand.Intn(8) 162 pageSize := 10 + rand.Intn(10) 163 164 shardID := rand.Int31() 165 treeID := primitives.NewUUID() 166 branchID := primitives.NewUUID() 167 168 nodeID := int64(1) 169 minNodeID := nodeID 170 maxNodeID := minNodeID + int64(numNodeIDs) 171 172 var nodes []sqlplugin.HistoryNodeRow 173 for i := 0; i < numNodeIDs; i++ { 174 for j := 0; j < nodePerNodeID; j++ { 175 node := s.newRandomNodeRow(shardID, treeID, branchID, nodeID, rand.Int63(), rand.Int63()) 176 result, err := s.store.InsertIntoHistoryNode(newExecutionContext(), &node) 177 s.NoError(err) 178 rowsAffected, err := result.RowsAffected() 179 s.NoError(err) 180 s.Equal(1, int(rowsAffected)) 181 nodes = append(nodes, node) 182 } 183 nodeID++ 184 } 185 186 selectFilter := sqlplugin.HistoryNodeSelectFilter{ 187 ShardID: shardID, 188 TreeID: treeID, 189 BranchID: branchID, 190 MinNodeID: minNodeID, 191 MinTxnID: sql.MinTxnID, 192 MaxNodeID: maxNodeID, 193 PageSize: pageSize, 194 } 195 var rows []sqlplugin.HistoryNodeRow 196 for { 197 rowsPerPage, err := s.store.RangeSelectFromHistoryNode(newExecutionContext(), selectFilter) 198 s.NoError(err) 199 rows = append(rows, rowsPerPage...) 200 201 if len(rowsPerPage) > 0 { 202 lastNode := rowsPerPage[len(rowsPerPage)-1] 203 selectFilter.MinNodeID = lastNode.NodeID 204 selectFilter.MinTxnID = lastNode.TxnID 205 } else { 206 break 207 } 208 } 209 210 // NOTE: TxnID is *= -1 within InsertIntoHistoryNode 211 for index := range nodes { 212 nodes[index].TxnID = -nodes[index].TxnID 213 } 214 sort.Slice(nodes, func(i, j int) bool { 215 this := nodes[i] 216 that := nodes[j] 217 218 if this.NodeID < that.NodeID { 219 return true 220 } else if this.NodeID > that.NodeID { 221 return false 222 } 223 224 // larger transaction ID means newer 225 if this.TxnID < that.TxnID { 226 return false 227 } else if this.TxnID > that.TxnID { 228 return true 229 } 230 231 // same 232 return true 233 }) 234 for index := range rows { 235 rows[index].ShardID = shardID 236 rows[index].TreeID = treeID 237 rows[index].BranchID = branchID 238 } 239 s.Equal(nodes, rows) 240 } 241 242 func (s *historyNodeSuite) TestDeleteSelect() { 243 pageSize := 100 244 245 shardID := rand.Int31() 246 treeID := primitives.NewUUID() 247 branchID := primitives.NewUUID() 248 nodeID := int64(1) 249 250 deleteFilter := sqlplugin.HistoryNodeDeleteFilter{ 251 ShardID: shardID, 252 TreeID: treeID, 253 BranchID: branchID, 254 MinNodeID: nodeID, 255 } 256 result, err := s.store.RangeDeleteFromHistoryNode(newExecutionContext(), deleteFilter) 257 s.NoError(err) 258 rowsAffected, err := result.RowsAffected() 259 s.NoError(err) 260 s.Equal(0, int(rowsAffected)) 261 262 selectFilter := sqlplugin.HistoryNodeSelectFilter{ 263 ShardID: shardID, 264 TreeID: treeID, 265 BranchID: branchID, 266 MinNodeID: nodeID, 267 MinTxnID: sql.MinTxnID, 268 MaxNodeID: math.MaxInt64, 269 PageSize: pageSize, 270 } 271 rows, err := s.store.RangeSelectFromHistoryNode(newExecutionContext(), selectFilter) 272 s.NoError(err) 273 for index := range rows { 274 rows[index].ShardID = shardID 275 rows[index].TreeID = treeID 276 rows[index].BranchID = branchID 277 } 278 s.Equal([]sqlplugin.HistoryNodeRow(nil), rows) 279 } 280 281 func (s *historyNodeSuite) TestInsertDeleteSelect_Single() { 282 pageSize := 100 283 284 shardID := rand.Int31() 285 treeID := primitives.NewUUID() 286 branchID := primitives.NewUUID() 287 nodeID := int64(1) 288 prevTransactionID := rand.Int63() 289 transactionID := rand.Int63() 290 291 node := s.newRandomNodeRow(shardID, treeID, branchID, nodeID, prevTransactionID, transactionID) 292 result, err := s.store.InsertIntoHistoryNode(newExecutionContext(), &node) 293 s.NoError(err) 294 rowsAffected, err := result.RowsAffected() 295 s.NoError(err) 296 s.Equal(1, int(rowsAffected)) 297 // transaction ID is *= -1 within InsertIntoHistoryNode 298 node.TxnID = -node.TxnID 299 300 result, err = s.store.DeleteFromHistoryNode(newExecutionContext(), &node) 301 s.NoError(err) 302 rowsAffected, err = result.RowsAffected() 303 s.NoError(err) 304 s.Equal(1, int(rowsAffected)) 305 306 selectFilter := sqlplugin.HistoryNodeSelectFilter{ 307 ShardID: shardID, 308 TreeID: treeID, 309 BranchID: branchID, 310 MinNodeID: nodeID, 311 MinTxnID: sql.MinTxnID, 312 MaxNodeID: math.MaxInt64, 313 PageSize: pageSize, 314 } 315 rows, err := s.store.RangeSelectFromHistoryNode(newExecutionContext(), selectFilter) 316 s.NoError(err) 317 for index := range rows { 318 rows[index].ShardID = shardID 319 rows[index].TreeID = treeID 320 rows[index].BranchID = branchID 321 } 322 s.Equal([]sqlplugin.HistoryNodeRow(nil), rows) 323 } 324 325 func (s *historyNodeSuite) TestInsertDeleteSelect_Multiple() { 326 numNodeIDs := 50 327 nodePerNodeID := 2 328 pageSize := 100 329 330 shardID := rand.Int31() 331 treeID := primitives.NewUUID() 332 branchID := primitives.NewUUID() 333 334 nodeID := int64(1) 335 minNodeID := nodeID 336 337 for i := 0; i < numNodeIDs; i++ { 338 for j := 0; j < nodePerNodeID; j++ { 339 node := s.newRandomNodeRow(shardID, treeID, branchID, nodeID, rand.Int63(), rand.Int63()) 340 result, err := s.store.InsertIntoHistoryNode(newExecutionContext(), &node) 341 s.NoError(err) 342 rowsAffected, err := result.RowsAffected() 343 s.NoError(err) 344 s.Equal(1, int(rowsAffected)) 345 } 346 nodeID++ 347 } 348 349 deleteFilter := sqlplugin.HistoryNodeDeleteFilter{ 350 ShardID: shardID, 351 TreeID: treeID, 352 BranchID: branchID, 353 MinNodeID: minNodeID, 354 } 355 result, err := s.store.RangeDeleteFromHistoryNode(newExecutionContext(), deleteFilter) 356 s.NoError(err) 357 rowsAffected, err := result.RowsAffected() 358 s.NoError(err) 359 s.Equal(numNodeIDs*nodePerNodeID, int(rowsAffected)) 360 361 selectFilter := sqlplugin.HistoryNodeSelectFilter{ 362 ShardID: shardID, 363 TreeID: treeID, 364 BranchID: branchID, 365 MinNodeID: nodeID, 366 MinTxnID: sql.MinTxnID, 367 MaxNodeID: math.MaxInt64, 368 PageSize: pageSize, 369 } 370 rows, err := s.store.RangeSelectFromHistoryNode(newExecutionContext(), selectFilter) 371 s.NoError(err) 372 for index := range rows { 373 rows[index].ShardID = shardID 374 rows[index].TreeID = treeID 375 rows[index].BranchID = branchID 376 } 377 s.Equal([]sqlplugin.HistoryNodeRow(nil), rows) 378 } 379 380 func (s *historyNodeSuite) newRandomNodeRow( 381 shardID int32, 382 treeID primitives.UUID, 383 branchID primitives.UUID, 384 nodeID int64, 385 prevTransactionID int64, 386 transactionID int64, 387 ) sqlplugin.HistoryNodeRow { 388 return sqlplugin.HistoryNodeRow{ 389 ShardID: shardID, 390 TreeID: treeID, 391 BranchID: branchID, 392 NodeID: nodeID, 393 PrevTxnID: prevTransactionID, 394 TxnID: transactionID, 395 Data: shuffle.Bytes(testHistoryNodeData), 396 DataEncoding: testHistoryNodeEncoding, 397 } 398 }