github.com/haalcala/mattermost-server-change-repo/v5@v5.33.2/services/cache/lru_test.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package cache 5 6 import ( 7 "fmt" 8 "sync" 9 "testing" 10 "time" 11 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 15 "github.com/mattermost/mattermost-server/v5/model" 16 ) 17 18 func TestLRU(t *testing.T) { 19 l := NewLRU(LRUOptions{ 20 Size: 128, 21 DefaultExpiry: 0, 22 InvalidateClusterEvent: "", 23 }) 24 25 for i := 0; i < 256; i++ { 26 err := l.Set(fmt.Sprintf("%d", i), i) 27 require.NoError(t, err) 28 } 29 size, err := l.Len() 30 require.NoError(t, err) 31 require.Equalf(t, size, 128, "bad len: %v", size) 32 33 keys, err := l.Keys() 34 require.NoError(t, err) 35 for i, k := range keys { 36 var v int 37 err = l.Get(k, &v) 38 require.NoError(t, err, "bad key: %v", k) 39 require.Equalf(t, fmt.Sprintf("%d", v), k, "bad key: %v", k) 40 require.Equalf(t, i+128, v, "bad value: %v", k) 41 } 42 for i := 0; i < 128; i++ { 43 var v int 44 err = l.Get(fmt.Sprintf("%d", i), &v) 45 require.Equal(t, ErrKeyNotFound, err, "should be evicted %v: %v", i, err) 46 } 47 for i := 128; i < 256; i++ { 48 var v int 49 err = l.Get(fmt.Sprintf("%d", i), &v) 50 require.NoError(t, err, "should not be evicted %v: %v", i, err) 51 } 52 for i := 128; i < 192; i++ { 53 l.Remove(fmt.Sprintf("%d", i)) 54 var v int 55 err = l.Get(fmt.Sprintf("%d", i), &v) 56 require.Equal(t, ErrKeyNotFound, err, "should be deleted %v: %v", i, err) 57 } 58 59 var v int 60 err = l.Get("192", &v) // expect 192 to be last key in l.Keys() 61 require.NoError(t, err, "should exist") 62 require.Equalf(t, 192, v, "bad value: %v", v) 63 64 keys, err = l.Keys() 65 require.NoError(t, err) 66 for i, k := range keys { 67 require.Falsef(t, i < 63 && k != fmt.Sprintf("%d", i+193), "out of order key: %v", k) 68 require.Falsef(t, i == 63 && k != "192", "out of order key: %v", k) 69 } 70 71 l.Purge() 72 size, err = l.Len() 73 require.NoError(t, err) 74 require.Equalf(t, size, 0, "bad len: %v", size) 75 err = l.Get("200", &v) 76 require.Equal(t, err, ErrKeyNotFound, "should contain nothing") 77 78 err = l.Set("201", 301) 79 require.NoError(t, err) 80 err = l.Get("201", &v) 81 require.NoError(t, err) 82 require.Equal(t, 301, v) 83 84 } 85 86 func TestLRUExpire(t *testing.T) { 87 l := NewLRU(LRUOptions{ 88 Size: 128, 89 DefaultExpiry: 1 * time.Second, 90 InvalidateClusterEvent: "", 91 }) 92 93 l.SetWithDefaultExpiry("1", 1) 94 l.SetWithExpiry("3", 3, 0*time.Second) 95 96 time.Sleep(time.Second * 2) 97 98 var r1 int 99 err := l.Get("1", &r1) 100 require.Equal(t, err, ErrKeyNotFound, "should not exist") 101 102 var r2 int 103 err2 := l.Get("3", &r2) 104 require.NoError(t, err2, "should exist") 105 require.Equal(t, 3, r2) 106 } 107 108 func TestLRUMarshalUnMarshal(t *testing.T) { 109 l := NewLRU(LRUOptions{ 110 Size: 1, 111 DefaultExpiry: 0, 112 InvalidateClusterEvent: "", 113 }) 114 115 value1 := map[string]interface{}{ 116 "key1": 1, 117 "key2": "value2", 118 } 119 err := l.Set("test", value1) 120 121 require.NoError(t, err) 122 123 var value2 map[string]interface{} 124 err = l.Get("test", &value2) 125 require.NoError(t, err) 126 assert.EqualValues(t, 1, value2["key1"]) 127 128 v2, ok := value2["key2"].(string) 129 require.True(t, ok, "unable to cast value") 130 assert.Equal(t, "value2", v2) 131 132 post := model.Post{ 133 Id: "id", 134 CreateAt: 11111, 135 UpdateAt: 11111, 136 DeleteAt: 11111, 137 EditAt: 111111, 138 IsPinned: true, 139 UserId: "UserId", 140 ChannelId: "ChannelId", 141 RootId: "RootId", 142 ParentId: "ParentId", 143 OriginalId: "OriginalId", 144 Message: "OriginalId", 145 MessageSource: "MessageSource", 146 Type: "Type", 147 Props: map[string]interface{}{ 148 "key": "val", 149 }, 150 Hashtags: "Hashtags", 151 Filenames: []string{"item1", "item2"}, 152 FileIds: []string{"item1", "item2"}, 153 PendingPostId: "PendingPostId", 154 HasReactions: true, 155 ReplyCount: 11111, 156 Metadata: &model.PostMetadata{ 157 Embeds: []*model.PostEmbed{ 158 { 159 Type: "Type", 160 URL: "URL", 161 Data: "some data", 162 }, 163 { 164 Type: "Type 2", 165 URL: "URL 2", 166 Data: "some data 2", 167 }, 168 }, 169 Emojis: []*model.Emoji{ 170 { 171 Id: "id", 172 Name: "name", 173 }, 174 }, 175 Files: nil, 176 Images: map[string]*model.PostImage{ 177 "key": { 178 Width: 1, 179 Height: 1, 180 Format: "format", 181 FrameCount: 1, 182 }, 183 "key2": { 184 Width: 999, 185 Height: 888, 186 Format: "format 2", 187 FrameCount: 1000, 188 }, 189 }, 190 Reactions: []*model.Reaction{ 191 { 192 UserId: "user_id", 193 PostId: "post_id", 194 EmojiName: "emoji_name", 195 CreateAt: 111, 196 }, 197 }, 198 }, 199 } 200 err = l.Set("post", post.Clone()) 201 require.NoError(t, err) 202 203 var p model.Post 204 err = l.Get("post", &p) 205 require.NoError(t, err) 206 require.Equal(t, post.Clone(), p.Clone()) 207 208 session := &model.Session{ 209 Id: "ty7ia14yuty5bmpt8wmz6da1fw", 210 Token: "79c3iq6nzpycmkkawudanqhg5c", 211 CreateAt: 1595445296960, 212 ExpiresAt: 1598296496960, 213 LastActivityAt: 1595445296960, 214 UserId: "rpgh1q5ra38y9xjn9z8fjctezr", 215 Roles: "system_admin system_user", 216 IsOAuth: false, 217 ExpiredNotify: false, 218 Props: map[string]string{ 219 "csrf": "33zb7h7rk3rfffztojn5pxbkxe", 220 "isMobile": "false", 221 "isSaml": "false", 222 "is_guest": "false", 223 "os": "", 224 "platform": "Windows", 225 }, 226 } 227 228 err = l.Set("session", session) 229 require.NoError(t, err) 230 var s = &model.Session{} 231 err = l.Get("session", s) 232 233 require.NoError(t, err) 234 require.Equal(t, session, s) 235 236 user := &model.User{ 237 Id: "id", 238 CreateAt: 11111, 239 UpdateAt: 11111, 240 DeleteAt: 11111, 241 Username: "username", 242 Password: "password", 243 AuthService: "AuthService", 244 AuthData: nil, 245 Email: "Email", 246 EmailVerified: true, 247 Nickname: "Nickname", 248 FirstName: "FirstName", 249 LastName: "LastName", 250 Position: "Position", 251 Roles: "Roles", 252 AllowMarketing: true, 253 Props: map[string]string{ 254 "key0": "value0", 255 }, 256 NotifyProps: map[string]string{ 257 "key0": "value0", 258 }, 259 LastPasswordUpdate: 111111, 260 LastPictureUpdate: 111111, 261 FailedAttempts: 111111, 262 Locale: "Locale", 263 MfaActive: true, 264 MfaSecret: "MfaSecret", 265 LastActivityAt: 111111, 266 IsBot: true, 267 TermsOfServiceId: "TermsOfServiceId", 268 TermsOfServiceCreateAt: 111111, 269 } 270 271 err = l.Set("user", user) 272 require.NoError(t, err) 273 274 var u *model.User 275 err = l.Get("user", &u) 276 require.NoError(t, err) 277 // msgp returns an empty map instead of a nil map. 278 // This does not make an actual difference in terms of functionality. 279 u.Timezone = nil 280 require.Equal(t, user, u) 281 282 tt := make(map[string]*model.User) 283 tt["1"] = u 284 err = l.Set("mm", model.UserMap(tt)) 285 require.NoError(t, err) 286 287 var out map[string]*model.User 288 err = l.Get("mm", &out) 289 require.NoError(t, err) 290 out["1"].Timezone = nil 291 require.Equal(t, tt, out) 292 } 293 294 func BenchmarkLRU(b *testing.B) { 295 296 value1 := "simplestring" 297 298 b.Run("simple=new", func(b *testing.B) { 299 for i := 0; i < b.N; i++ { 300 l2 := NewLRU(LRUOptions{ 301 Size: 1, 302 DefaultExpiry: 0, 303 InvalidateClusterEvent: "", 304 }) 305 err := l2.Set("test", value1) 306 require.NoError(b, err) 307 308 var val string 309 err = l2.Get("test", &val) 310 require.NoError(b, err) 311 } 312 }) 313 314 type obj struct { 315 Field1 int 316 Field2 string 317 Field3 struct { 318 Field4 int 319 Field5 string 320 } 321 Field6 map[string]string 322 } 323 324 value2 := obj{ 325 1, 326 "field2", 327 struct { 328 Field4 int 329 Field5 string 330 }{ 331 6, 332 "field5 is a looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string", 333 }, 334 map[string]string{ 335 "key0": "value0", 336 "key1": "value value1", 337 "key2": "value value value2", 338 "key3": "value value value value3", 339 "key4": "value value value value value4", 340 "key5": "value value value value value value5", 341 "key6": "value value value value value value value6", 342 "key7": "value value value value value value value value7", 343 "key8": "value value value value value value value value value8", 344 "key9": "value value value value value value value value value value9", 345 }, 346 } 347 348 b.Run("complex=new", func(b *testing.B) { 349 for i := 0; i < b.N; i++ { 350 l2 := NewLRU(LRUOptions{ 351 Size: 1, 352 DefaultExpiry: 0, 353 InvalidateClusterEvent: "", 354 }) 355 err := l2.Set("test", value2) 356 require.NoError(b, err) 357 358 var val obj 359 err = l2.Get("test", &val) 360 require.NoError(b, err) 361 } 362 }) 363 364 user := &model.User{ 365 Id: "id", 366 CreateAt: 11111, 367 UpdateAt: 11111, 368 DeleteAt: 11111, 369 Username: "username", 370 Password: "password", 371 AuthService: "AuthService", 372 AuthData: nil, 373 Email: "Email", 374 EmailVerified: true, 375 Nickname: "Nickname", 376 FirstName: "FirstName", 377 LastName: "LastName", 378 Position: "Position", 379 Roles: "Roles", 380 AllowMarketing: true, 381 Props: map[string]string{ 382 "key0": "value0", 383 "key1": "value value1", 384 "key2": "value value value2", 385 "key3": "value value value value3", 386 "key4": "value value value value value4", 387 "key5": "value value value value value value5", 388 "key6": "value value value value value value value6", 389 "key7": "value value value value value value value value7", 390 "key8": "value value value value value value value value value8", 391 "key9": "value value value value value value value value value value9", 392 }, 393 NotifyProps: map[string]string{ 394 "key0": "value0", 395 "key1": "value value1", 396 "key2": "value value value2", 397 "key3": "value value value value3", 398 "key4": "value value value value value4", 399 "key5": "value value value value value value5", 400 "key6": "value value value value value value value6", 401 "key7": "value value value value value value value value7", 402 "key8": "value value value value value value value value value8", 403 "key9": "value value value value value value value value value value9", 404 }, 405 LastPasswordUpdate: 111111, 406 LastPictureUpdate: 111111, 407 FailedAttempts: 111111, 408 Locale: "Locale", 409 Timezone: map[string]string{ 410 "key0": "value0", 411 "key1": "value value1", 412 "key2": "value value value2", 413 "key3": "value value value value3", 414 "key4": "value value value value value4", 415 "key5": "value value value value value value5", 416 "key6": "value value value value value value value6", 417 "key7": "value value value value value value value value7", 418 "key8": "value value value value value value value value value8", 419 "key9": "value value value value value value value value value value9", 420 }, 421 MfaActive: true, 422 MfaSecret: "MfaSecret", 423 LastActivityAt: 111111, 424 IsBot: true, 425 BotDescription: "field5 is a looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string", 426 BotLastIconUpdate: 111111, 427 TermsOfServiceId: "TermsOfServiceId", 428 TermsOfServiceCreateAt: 111111, 429 } 430 431 b.Run("User=new", func(b *testing.B) { 432 for i := 0; i < b.N; i++ { 433 l2 := NewLRU(LRUOptions{ 434 Size: 1, 435 DefaultExpiry: 0, 436 InvalidateClusterEvent: "", 437 }) 438 err := l2.Set("test", user) 439 require.NoError(b, err) 440 441 var val model.User 442 err = l2.Get("test", &val) 443 require.NoError(b, err) 444 } 445 }) 446 447 uMap := map[string]*model.User{ 448 "id1": { 449 Id: "id1", 450 CreateAt: 1111, 451 UpdateAt: 1112, 452 Username: "user1", 453 Password: "pass", 454 }, 455 "id2": { 456 Id: "id2", 457 CreateAt: 1113, 458 UpdateAt: 1114, 459 Username: "user2", 460 Password: "pass2", 461 }, 462 } 463 464 b.Run("UserMap=new", func(b *testing.B) { 465 for i := 0; i < b.N; i++ { 466 l2 := NewLRU(LRUOptions{ 467 Size: 1, 468 DefaultExpiry: 0, 469 InvalidateClusterEvent: "", 470 }) 471 err := l2.Set("test", model.UserMap(uMap)) 472 require.NoError(b, err) 473 474 var val map[string]*model.User 475 err = l2.Get("test", &val) 476 require.NoError(b, err) 477 } 478 }) 479 480 post := &model.Post{ 481 Id: "id", 482 CreateAt: 11111, 483 UpdateAt: 11111, 484 DeleteAt: 11111, 485 EditAt: 111111, 486 IsPinned: true, 487 UserId: "UserId", 488 ChannelId: "ChannelId", 489 RootId: "RootId", 490 ParentId: "ParentId", 491 OriginalId: "OriginalId", 492 Message: "OriginalId", 493 MessageSource: "MessageSource", 494 Type: "Type", 495 Props: map[string]interface{}{ 496 "key": "val", 497 }, 498 Hashtags: "Hashtags", 499 Filenames: []string{"item1", "item2"}, 500 FileIds: []string{"item1", "item2"}, 501 PendingPostId: "PendingPostId", 502 HasReactions: true, 503 504 // Transient data populated before sending a post to the client 505 ReplyCount: 11111, 506 Metadata: &model.PostMetadata{ 507 Embeds: []*model.PostEmbed{ 508 { 509 Type: "Type", 510 URL: "URL", 511 Data: "some data", 512 }, 513 { 514 Type: "Type 2", 515 URL: "URL 2", 516 Data: "some data 2", 517 }, 518 }, 519 Emojis: []*model.Emoji{ 520 { 521 Id: "id", 522 Name: "name", 523 }, 524 }, 525 Files: nil, 526 Images: map[string]*model.PostImage{ 527 "key": { 528 Width: 1, 529 Height: 1, 530 Format: "format", 531 FrameCount: 1, 532 }, 533 "key2": { 534 Width: 999, 535 Height: 888, 536 Format: "format 2", 537 FrameCount: 1000, 538 }, 539 }, 540 Reactions: []*model.Reaction{}, 541 }, 542 } 543 544 b.Run("Post=new", func(b *testing.B) { 545 for i := 0; i < b.N; i++ { 546 l2 := NewLRU(LRUOptions{ 547 Size: 1, 548 DefaultExpiry: 0, 549 InvalidateClusterEvent: "", 550 }) 551 err := l2.Set("test", post) 552 require.NoError(b, err) 553 554 var val model.Post 555 err = l2.Get("test", &val) 556 require.NoError(b, err) 557 } 558 }) 559 560 status := model.Status{ 561 UserId: "UserId", 562 Status: "Status", 563 Manual: true, 564 LastActivityAt: 111111, 565 ActiveChannel: "ActiveChannel", 566 } 567 568 b.Run("Status=new", func(b *testing.B) { 569 for i := 0; i < b.N; i++ { 570 l2 := NewLRU(LRUOptions{ 571 Size: 1, 572 DefaultExpiry: 0, 573 InvalidateClusterEvent: "", 574 }) 575 err := l2.Set("test", status) 576 require.NoError(b, err) 577 578 var val *model.Status 579 err = l2.Get("test", &val) 580 require.NoError(b, err) 581 } 582 }) 583 584 session := model.Session{ 585 Id: "ty7ia14yuty5bmpt8wmz6da1fw", 586 Token: "79c3iq6nzpycmkkawudanqhg5c", 587 CreateAt: 1595445296960, 588 ExpiresAt: 1598296496960, 589 LastActivityAt: 1595445296960, 590 UserId: "rpgh1q5ra38y9xjn9z8fjctezr", 591 Roles: "system_admin system_user", 592 IsOAuth: false, 593 ExpiredNotify: false, 594 Props: map[string]string{ 595 "csrf": "33zb7h7rk3rfffztojn5pxbkxe", 596 "isMobile": "false", 597 "isSaml": "false", 598 "is_guest": "false", 599 "os": "", 600 "platform": "Windows", 601 }, 602 } 603 604 b.Run("Session=new", func(b *testing.B) { 605 for i := 0; i < b.N; i++ { 606 l2 := NewLRU(LRUOptions{ 607 Size: 1, 608 DefaultExpiry: 0, 609 InvalidateClusterEvent: "", 610 }) 611 err := l2.Set("test", &session) 612 require.NoError(b, err) 613 614 var val *model.Session 615 err = l2.Get("test", &val) 616 require.NoError(b, err) 617 } 618 }) 619 } 620 621 func TestLRURace(t *testing.T) { 622 l2 := NewLRU(LRUOptions{ 623 Size: 1, 624 DefaultExpiry: 0, 625 InvalidateClusterEvent: "", 626 }) 627 var wg sync.WaitGroup 628 l2.Set("test", "value1") 629 630 wg.Add(2) 631 632 go func() { 633 defer wg.Done() 634 value1 := "simplestring" 635 err := l2.Set("test", value1) 636 require.NoError(t, err) 637 }() 638 639 go func() { 640 defer wg.Done() 641 642 var val string 643 err := l2.Get("test", &val) 644 require.NoError(t, err) 645 }() 646 647 wg.Wait() 648 }