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  }