github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/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 "testing" 9 "time" 10 11 "github.com/mattermost/mattermost-server/v5/model" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 ) 15 16 func TestLRU(t *testing.T) { 17 l := NewLRU(&LRUOptions{ 18 Size: 128, 19 DefaultExpiry: 0, 20 InvalidateClusterEvent: "", 21 }) 22 23 for i := 0; i < 256; i++ { 24 err := l.Set(fmt.Sprintf("%d", i), i) 25 require.Nil(t, err) 26 } 27 size, err := l.Len() 28 require.Nil(t, err) 29 require.Equalf(t, size, 128, "bad len: %v", size) 30 31 keys, err := l.Keys() 32 require.Nil(t, err) 33 for i, k := range keys { 34 var v int 35 err = l.Get(k, &v) 36 require.Nil(t, err, "bad key: %v", k) 37 require.Equalf(t, fmt.Sprintf("%d", v), k, "bad key: %v", k) 38 require.Equalf(t, i+128, v, "bad value: %v", k) 39 } 40 for i := 0; i < 128; i++ { 41 var v int 42 err = l.Get(fmt.Sprintf("%d", i), &v) 43 require.Equal(t, ErrKeyNotFound, err, "should be evicted %v: %v", i, err) 44 } 45 for i := 128; i < 256; i++ { 46 var v int 47 err = l.Get(fmt.Sprintf("%d", i), &v) 48 require.Nil(t, err, "should not be evicted %v: %v", i, err) 49 } 50 for i := 128; i < 192; i++ { 51 l.Remove(fmt.Sprintf("%d", i)) 52 var v int 53 err = l.Get(fmt.Sprintf("%d", i), &v) 54 require.Equal(t, ErrKeyNotFound, err, "should be deleted %v: %v", i, err) 55 } 56 57 var v int 58 err = l.Get("192", &v) // expect 192 to be last key in l.Keys() 59 require.Nil(t, err, "should exist") 60 require.Equalf(t, 192, v, "bad value: %v", v) 61 62 keys, err = l.Keys() 63 require.Nil(t, err) 64 for i, k := range keys { 65 require.Falsef(t, i < 63 && k != fmt.Sprintf("%d", i+193), "out of order key: %v", k) 66 require.Falsef(t, i == 63 && k != "192", "out of order key: %v", k) 67 } 68 69 l.Purge() 70 size, err = l.Len() 71 require.Nil(t, err) 72 require.Equalf(t, size, 0, "bad len: %v", size) 73 err = l.Get("200", &v) 74 require.Equal(t, err, ErrKeyNotFound, "should contain nothing") 75 76 err = l.Set("201", 301) 77 require.Nil(t, err) 78 err = l.Get("201", &v) 79 require.Nil(t, err) 80 require.Equal(t, 301, v) 81 82 } 83 84 func TestLRUExpire(t *testing.T) { 85 l := NewLRU(&LRUOptions{ 86 Size: 128, 87 DefaultExpiry: 1 * time.Second, 88 InvalidateClusterEvent: "", 89 }) 90 91 l.SetWithDefaultExpiry("1", 1) 92 l.SetWithExpiry("3", 3, 0*time.Second) 93 94 time.Sleep(time.Second * 2) 95 96 var r1 int 97 err := l.Get("1", &r1) 98 require.Equal(t, err, ErrKeyNotFound, "should not exist") 99 100 var r2 int 101 err2 := l.Get("3", &r2) 102 require.Nil(t, err2, "should exist") 103 require.Equal(t, 3, r2) 104 } 105 106 func TestLRUMarshalUnMarshal(t *testing.T) { 107 l := NewLRU(&LRUOptions{ 108 Size: 1, 109 DefaultExpiry: 0, 110 InvalidateClusterEvent: "", 111 }) 112 113 value1 := map[string]interface{}{ 114 "key1": 1, 115 "key2": "value2", 116 } 117 err := l.Set("test", value1) 118 119 require.Nil(t, err) 120 121 var value2 map[string]interface{} 122 err = l.Get("test", &value2) 123 require.Nil(t, err) 124 125 v1, ok := value2["key1"].(int64) 126 require.True(t, ok, "unable to cast value") 127 assert.Equal(t, int64(1), v1) 128 129 v2, ok := value2["key2"].(string) 130 require.True(t, ok, "unable to cast value") 131 assert.Equal(t, "value2", v2) 132 133 post := model.Post{ 134 Id: "id", 135 CreateAt: 11111, 136 UpdateAt: 11111, 137 DeleteAt: 11111, 138 EditAt: 111111, 139 IsPinned: true, 140 UserId: "UserId", 141 ChannelId: "ChannelId", 142 RootId: "RootId", 143 ParentId: "ParentId", 144 OriginalId: "OriginalId", 145 Message: "OriginalId", 146 MessageSource: "MessageSource", 147 Type: "Type", 148 Props: map[string]interface{}{ 149 "key": "val", 150 }, 151 Hashtags: "Hashtags", 152 Filenames: []string{"item1", "item2"}, 153 FileIds: []string{"item1", "item2"}, 154 PendingPostId: "PendingPostId", 155 HasReactions: true, 156 ReplyCount: 11111, 157 Metadata: &model.PostMetadata{ 158 Embeds: []*model.PostEmbed{ 159 { 160 Type: "Type", 161 URL: "URL", 162 Data: "some data", 163 }, 164 { 165 Type: "Type 2", 166 URL: "URL 2", 167 Data: "some data 2", 168 }, 169 }, 170 Emojis: []*model.Emoji{ 171 { 172 Id: "id", 173 Name: "name", 174 }, 175 }, 176 Files: nil, 177 Images: map[string]*model.PostImage{ 178 "key": { 179 Width: 1, 180 Height: 1, 181 Format: "format", 182 FrameCount: 1, 183 }, 184 "key2": { 185 Width: 999, 186 Height: 888, 187 Format: "format 2", 188 FrameCount: 1000, 189 }, 190 }, 191 Reactions: []*model.Reaction{ 192 { 193 UserId: "user_id", 194 PostId: "post_id", 195 EmojiName: "emoji_name", 196 CreateAt: 111, 197 }, 198 }, 199 }, 200 } 201 err = l.Set("post", post.Clone()) 202 require.Nil(t, err) 203 204 var p model.Post 205 err = l.Get("post", &p) 206 require.Nil(t, err) 207 require.Equal(t, post.Clone(), p.Clone()) 208 } 209 210 func BenchmarkLRU(b *testing.B) { 211 212 value1 := "simplestring" 213 214 b.Run("simple=new", func(b *testing.B) { 215 for i := 0; i < b.N; i++ { 216 l2 := NewLRU(&LRUOptions{ 217 Size: 1, 218 DefaultExpiry: 0, 219 InvalidateClusterEvent: "", 220 }) 221 err := l2.Set("test", value1) 222 require.Nil(b, err) 223 224 var val string 225 err = l2.Get("test", &val) 226 require.Nil(b, err) 227 } 228 }) 229 230 type obj struct { 231 Field1 int 232 Field2 string 233 Field3 struct { 234 Field4 int 235 Field5 string 236 } 237 Field6 map[string]string 238 } 239 240 value2 := obj{ 241 1, 242 "field2", 243 struct { 244 Field4 int 245 Field5 string 246 }{ 247 6, 248 "field5 is a looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string", 249 }, 250 map[string]string{ 251 "key0": "value0", 252 "key1": "value value1", 253 "key2": "value value value2", 254 "key3": "value value value value3", 255 "key4": "value value value value value4", 256 "key5": "value value value value value value5", 257 "key6": "value value value value value value value6", 258 "key7": "value value value value value value value value7", 259 "key8": "value value value value value value value value value8", 260 "key9": "value value value value value value value value value value9", 261 }, 262 } 263 264 b.Run("complex=new", func(b *testing.B) { 265 for i := 0; i < b.N; i++ { 266 l2 := NewLRU(&LRUOptions{ 267 Size: 1, 268 DefaultExpiry: 0, 269 InvalidateClusterEvent: "", 270 }) 271 err := l2.Set("test", value2) 272 require.Nil(b, err) 273 274 var val obj 275 err = l2.Get("test", &val) 276 require.Nil(b, err) 277 } 278 }) 279 280 user := &model.User{ 281 Id: "id", 282 CreateAt: 11111, 283 UpdateAt: 11111, 284 DeleteAt: 11111, 285 Username: "username", 286 Password: "password", 287 AuthService: "AuthService", 288 AuthData: nil, 289 Email: "Email", 290 EmailVerified: true, 291 Nickname: "Nickname", 292 FirstName: "FirstName", 293 LastName: "LastName", 294 Position: "Position", 295 Roles: "Roles", 296 AllowMarketing: true, 297 Props: map[string]string{ 298 "key0": "value0", 299 "key1": "value value1", 300 "key2": "value value value2", 301 "key3": "value value value value3", 302 "key4": "value value value value value4", 303 "key5": "value value value value value value5", 304 "key6": "value value value value value value value6", 305 "key7": "value value value value value value value value7", 306 "key8": "value value value value value value value value value8", 307 "key9": "value value value value value value value value value value9", 308 }, 309 NotifyProps: map[string]string{ 310 "key0": "value0", 311 "key1": "value value1", 312 "key2": "value value value2", 313 "key3": "value value value value3", 314 "key4": "value value value value value4", 315 "key5": "value value value value value value5", 316 "key6": "value value value value value value value6", 317 "key7": "value value value value value value value value7", 318 "key8": "value value value value value value value value value8", 319 "key9": "value value value value value value value value value value9", 320 }, 321 LastPasswordUpdate: 111111, 322 LastPictureUpdate: 111111, 323 FailedAttempts: 111111, 324 Locale: "Locale", 325 Timezone: map[string]string{ 326 "key0": "value0", 327 "key1": "value value1", 328 "key2": "value value value2", 329 "key3": "value value value value3", 330 "key4": "value value value value value4", 331 "key5": "value value value value value value5", 332 "key6": "value value value value value value value6", 333 "key7": "value value value value value value value value7", 334 "key8": "value value value value value value value value value8", 335 "key9": "value value value value value value value value value value9", 336 }, 337 MfaActive: true, 338 MfaSecret: "MfaSecret", 339 LastActivityAt: 111111, 340 IsBot: true, 341 BotDescription: "field5 is a looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong string", 342 BotLastIconUpdate: 111111, 343 TermsOfServiceId: "TermsOfServiceId", 344 TermsOfServiceCreateAt: 111111, 345 } 346 347 b.Run("User=new", func(b *testing.B) { 348 for i := 0; i < b.N; i++ { 349 l2 := NewLRU(&LRUOptions{ 350 Size: 1, 351 DefaultExpiry: 0, 352 InvalidateClusterEvent: "", 353 }) 354 err := l2.Set("test", user) 355 require.Nil(b, err) 356 357 var val model.User 358 err = l2.Get("test", &val) 359 require.Nil(b, err) 360 } 361 }) 362 363 post := &model.Post{ 364 Id: "id", 365 CreateAt: 11111, 366 UpdateAt: 11111, 367 DeleteAt: 11111, 368 EditAt: 111111, 369 IsPinned: true, 370 UserId: "UserId", 371 ChannelId: "ChannelId", 372 RootId: "RootId", 373 ParentId: "ParentId", 374 OriginalId: "OriginalId", 375 Message: "OriginalId", 376 MessageSource: "MessageSource", 377 Type: "Type", 378 Props: map[string]interface{}{ 379 "key": "val", 380 }, 381 Hashtags: "Hashtags", 382 Filenames: []string{"item1", "item2"}, 383 FileIds: []string{"item1", "item2"}, 384 PendingPostId: "PendingPostId", 385 HasReactions: true, 386 387 // Transient data populated before sending a post to the client 388 ReplyCount: 11111, 389 Metadata: &model.PostMetadata{ 390 Embeds: []*model.PostEmbed{ 391 { 392 Type: "Type", 393 URL: "URL", 394 Data: "some data", 395 }, 396 { 397 Type: "Type 2", 398 URL: "URL 2", 399 Data: "some data 2", 400 }, 401 }, 402 Emojis: []*model.Emoji{ 403 { 404 Id: "id", 405 Name: "name", 406 }, 407 }, 408 Files: nil, 409 Images: map[string]*model.PostImage{ 410 "key": { 411 Width: 1, 412 Height: 1, 413 Format: "format", 414 FrameCount: 1, 415 }, 416 "key2": { 417 Width: 999, 418 Height: 888, 419 Format: "format 2", 420 FrameCount: 1000, 421 }, 422 }, 423 Reactions: []*model.Reaction{}, 424 }, 425 } 426 427 b.Run("Post=new", func(b *testing.B) { 428 for i := 0; i < b.N; i++ { 429 l2 := NewLRU(&LRUOptions{ 430 Size: 1, 431 DefaultExpiry: 0, 432 InvalidateClusterEvent: "", 433 }) 434 err := l2.Set("test", post) 435 require.Nil(b, err) 436 437 var val model.Post 438 err = l2.Get("test", &val) 439 require.Nil(b, err) 440 } 441 }) 442 443 status := model.Status{ 444 UserId: "UserId", 445 Status: "Status", 446 Manual: true, 447 LastActivityAt: 111111, 448 ActiveChannel: "ActiveChannel", 449 } 450 451 b.Run("Status=new", func(b *testing.B) { 452 for i := 0; i < b.N; i++ { 453 l2 := NewLRU(&LRUOptions{ 454 Size: 1, 455 DefaultExpiry: 0, 456 InvalidateClusterEvent: "", 457 }) 458 err := l2.Set("test", status) 459 require.Nil(b, err) 460 461 var val model.Status 462 err = l2.Get("test", &val) 463 require.Nil(b, err) 464 } 465 }) 466 }