github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/util/active_user_test.go (about)

     1  package util
     2  
     3  import (
     4  	"fmt"
     5  	"runtime"
     6  	"strconv"
     7  	"sync"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/stretchr/testify/require"
    12  	"go.uber.org/atomic"
    13  )
    14  
    15  func TestActiveUser(t *testing.T) {
    16  	as := NewActiveUsers()
    17  	as.UpdateUserTimestamp("test1", 5)
    18  	as.UpdateUserTimestamp("test2", 10)
    19  	as.UpdateUserTimestamp("test3", 15)
    20  
    21  	require.Nil(t, as.PurgeInactiveUsers(2))
    22  	require.Equal(t, []string{"test1"}, as.PurgeInactiveUsers(5))
    23  	require.Nil(t, as.PurgeInactiveUsers(7))
    24  	require.Equal(t, []string{"test2"}, as.PurgeInactiveUsers(12))
    25  
    26  	as.UpdateUserTimestamp("test1", 17)
    27  	require.Equal(t, []string{"test3"}, as.PurgeInactiveUsers(16))
    28  	require.Equal(t, []string{"test1"}, as.PurgeInactiveUsers(20))
    29  }
    30  
    31  func TestActiveUserConcurrentUpdateAndPurge(t *testing.T) {
    32  	count := 10
    33  
    34  	as := NewActiveUsers()
    35  
    36  	done := sync.WaitGroup{}
    37  	stop := atomic.NewBool(false)
    38  
    39  	latestTS := atomic.NewInt64(0)
    40  
    41  	for j := 0; j < count; j++ {
    42  		done.Add(1)
    43  
    44  		go func() {
    45  			defer done.Done()
    46  
    47  			for !stop.Load() {
    48  				ts := latestTS.Inc()
    49  
    50  				// In each cycle, we update different user.
    51  				as.UpdateUserTimestamp(fmt.Sprintf("%d", ts), ts)
    52  
    53  				time.Sleep(1 * time.Millisecond)
    54  			}
    55  		}()
    56  	}
    57  
    58  	previousLatest := int64(0)
    59  	for i := 0; i < 10; i++ {
    60  		time.Sleep(100 * time.Millisecond)
    61  
    62  		latest := latestTS.Load()
    63  		require.True(t, latest > previousLatest)
    64  
    65  		previousLatest = latest
    66  
    67  		purged := as.PurgeInactiveUsers(latest)
    68  		require.NotEmpty(t, purged)
    69  	}
    70  
    71  	stop.Store(true)
    72  	done.Wait()
    73  
    74  	// Final purge.
    75  	latest := latestTS.Load()
    76  	as.PurgeInactiveUsers(latest)
    77  
    78  	// Purging again doesn't do anything.
    79  	purged := as.PurgeInactiveUsers(latest)
    80  	require.Empty(t, purged)
    81  }
    82  
    83  func BenchmarkActiveUsers_UpdateUserTimestamp(b *testing.B) {
    84  	for _, c := range []int{0, 5, 10, 25, 50, 100} {
    85  		b.Run(strconv.Itoa(c), func(b *testing.B) {
    86  			as := NewActiveUsers()
    87  
    88  			startGoroutinesDoingUpdates(b, c, as)
    89  
    90  			for i := 0; i < b.N; i++ {
    91  				as.UpdateUserTimestamp("test", int64(i))
    92  			}
    93  		})
    94  	}
    95  }
    96  
    97  func BenchmarkActiveUsers_Purge(b *testing.B) {
    98  	for _, c := range []int{0, 5, 10, 25, 50, 100} {
    99  		b.Run(strconv.Itoa(c), func(b *testing.B) {
   100  			as := NewActiveUsers()
   101  
   102  			startGoroutinesDoingUpdates(b, c, as)
   103  
   104  			for i := 0; i < b.N; i++ {
   105  				as.PurgeInactiveUsers(int64(i))
   106  			}
   107  		})
   108  	}
   109  }
   110  
   111  func startGoroutinesDoingUpdates(b *testing.B, count int, as *ActiveUsers) {
   112  	done := sync.WaitGroup{}
   113  	stop := atomic.NewBool(false)
   114  
   115  	started := sync.WaitGroup{}
   116  	for j := 0; j < count; j++ {
   117  		done.Add(1)
   118  		started.Add(1)
   119  		userID := fmt.Sprintf("user-%d", j)
   120  		go func() {
   121  			defer done.Done()
   122  			started.Done()
   123  
   124  			ts := int64(0)
   125  			for !stop.Load() {
   126  				ts++
   127  				as.UpdateUserTimestamp(userID, ts)
   128  
   129  				// Give other goroutines a chance too.
   130  				if ts%1000 == 0 {
   131  					runtime.Gosched()
   132  				}
   133  			}
   134  		}()
   135  	}
   136  	started.Wait()
   137  
   138  	b.Cleanup(func() {
   139  		// Ask goroutines to stop, and then wait until they do.
   140  		stop.Store(true)
   141  		done.Wait()
   142  	})
   143  }