Update clientcountutil to accommodate entity record usage time changes CE changes (#31301)

* moving clientcountutil changes from ent

* adding random usage time for repeated clients

* replace math.rand with crypto.rand
This commit is contained in:
akshya96 2025-07-22 10:20:10 -07:00 committed by GitHub
parent 8309387230
commit 1fb006633b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 99 additions and 8 deletions

View File

@ -4,8 +4,10 @@
package timeutil
import (
"crypto/rand"
"errors"
"fmt"
"math/big"
"strconv"
"strings"
"testing"
@ -189,3 +191,47 @@ func NormalizeToYear(date, normal time.Time) time.Time {
}
return date
}
// GetRandomTimeInMonth gets a random time in same month as input time
func GetRandomTimeInMonth(inputTime time.Time) (time.Time, error) {
firstOfMonth := StartOfMonth(inputTime)
year, month, _ := firstOfMonth.Date()
// Get the last day of the target month
_, _, lastDayOfMonth := EndOfMonth(inputTime).Date()
// Generate random day, hour, minute, and second
maxDay := big.NewInt(int64(lastDayOfMonth))
randomDayPtr, err := rand.Int(rand.Reader, maxDay)
if err != nil {
return time.Time{}, err
}
maxHour := big.NewInt(int64(24))
randomHourPtr, err := rand.Int(rand.Reader, maxHour)
if err != nil {
return time.Time{}, err
}
maxMinute := big.NewInt(int64(60))
randomMinutePtr, err := rand.Int(rand.Reader, maxMinute)
if err != nil {
return time.Time{}, err
}
maxSecond := big.NewInt(int64(60))
randomSecondPtr, err := rand.Int(rand.Reader, maxSecond)
if err != nil {
return time.Time{}, err
}
randomDay := int(randomDayPtr.Int64() + 1) // +1 because rand.Int returns [0, n)
randomHour := int(randomHourPtr.Int64())
randomMinute := int(randomMinutePtr.Int64())
randomSecond := int(randomSecondPtr.Int64())
// Create the random time
randomTime := time.Date(year, month, randomDay, randomHour, randomMinute, randomSecond, 0, time.UTC)
return randomTime, nil
}

View File

@ -460,3 +460,38 @@ func TestTimeUtil_NormalizeToYear(t *testing.T) {
require.Equal(t, tc.expectedNormalizedDate, normalizedDate)
}
}
// Test_GetRandomTimeInMonth verifies that the random time generated is in the same month of input time.
func Test_GetRandomTimeInMonth(t *testing.T) {
currYear, _, _ := time.Now().Date()
middleOfMonth := time.Date(currYear, 8, 15, 1, 2, 3, 0, time.UTC)
cases := []struct {
name string
inputTime time.Time
}{
{
name: "input time is in the start of a month",
inputTime: StartOfMonth(time.Now()),
},
{
name: "input time is in the middle of a month",
inputTime: middleOfMonth,
},
{
name: "input time is in the last of a month ",
inputTime: EndOfMonth(time.Now()),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
randomTime, err := GetRandomTimeInMonth(tc.inputTime)
require.NoError(t, err)
startOfMonth := StartOfMonth(tc.inputTime)
endOfMonth := EndOfMonth(tc.inputTime)
if !((randomTime.After(startOfMonth) || randomTime.Equal(startOfMonth)) && (randomTime.Before(endOfMonth) || randomTime.Equal(endOfMonth))) {
t.Fatalf("random time %v is not in the same month as input time %v", randomTime.UTC(), tc.inputTime.UTC())
}
})
}
}

View File

@ -225,6 +225,7 @@ func (s *singleMonthActivityClients) addNewClients(c *generation.Client, mountAc
NonEntity: isNonEntity,
ClientType: c.ClientType,
Timestamp: ts.Unix(),
UsageTime: ts.Unix(),
}
if record.ClientID == "" {
var err error
@ -330,12 +331,12 @@ func (m *multipleMonthsActivityClients) processMonth(ctx context.Context, core *
func (m *multipleMonthsActivityClients) addClientToMonth(monthsAgo int32, c *generation.Client, mountAccessor string, segmentIndex *int, now time.Time) error {
if c.Repeated || c.RepeatedFromMonth > 0 {
return m.addRepeatedClients(monthsAgo, c, mountAccessor, segmentIndex)
return m.addRepeatedClients(monthsAgo, c, mountAccessor, segmentIndex, now)
}
return m.months[monthsAgo].addNewClients(c, mountAccessor, segmentIndex, monthsAgo, now)
}
func (m *multipleMonthsActivityClients) addRepeatedClients(monthsAgo int32, c *generation.Client, mountAccessor string, segmentIndex *int) error {
func (m *multipleMonthsActivityClients) addRepeatedClients(monthsAgo int32, c *generation.Client, mountAccessor string, segmentIndex *int, now time.Time) error {
addingTo := m.months[monthsAgo]
repeatedFromMonth := monthsAgo + 1
if c.RepeatedFromMonth > 0 {
@ -346,8 +347,17 @@ func (m *multipleMonthsActivityClients) addRepeatedClients(monthsAgo int32, c *g
if c.Count > 0 {
numClients = int(c.Count)
}
// usage time of the client in the month that the client is being added to
// this is a random time in the usage month
usageTime, err := timeutil.GetRandomTimeInMonth(timeutil.MonthsPreviousTo(int(monthsAgo), now))
if err != nil {
return err
}
for _, client := range repeatedFrom.clients {
if c.ClientType == client.ClientType && mountAccessor == client.MountAccessor && c.Namespace == client.NamespaceID {
client.UsageTime = usageTime.Unix()
addingTo.addEntityRecord(client, segmentIndex)
numClients--
if numClients == 0 {

View File

@ -354,26 +354,26 @@ func Test_multipleMonthsActivityClients_addRepeatedClients(t *testing.T) {
thisMonth := m.months[0]
// this will match the first client in month 1
require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 1, Repeated: true}, defaultMount, nil))
require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 1, Repeated: true}, defaultMount, nil, time.Now().UTC()))
require.Contains(t, month1Clients, thisMonth.clients[0])
// this will match the 3rd client in month 1
require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 1, Repeated: true, ClientType: "non-entity"}, defaultMount, nil))
require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 1, Repeated: true, ClientType: "non-entity"}, defaultMount, nil, time.Now().UTC()))
require.Equal(t, month1Clients[2], thisMonth.clients[1])
// this will match the first two clients in month 1
require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 2, Repeated: true}, defaultMount, nil))
require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 2, Repeated: true}, defaultMount, nil, time.Now().UTC()))
require.Equal(t, month1Clients[0:2], thisMonth.clients[2:4])
// this will match the first client in month 2
require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 1, RepeatedFromMonth: 2}, "identity", nil))
require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 1, RepeatedFromMonth: 2}, "identity", nil, time.Now().UTC()))
require.Equal(t, month2Clients[0], thisMonth.clients[4])
// this will match the 3rd client in month 2
require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 1, RepeatedFromMonth: 2, Namespace: "other_ns"}, defaultMount, nil))
require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 1, RepeatedFromMonth: 2, Namespace: "other_ns"}, defaultMount, nil, time.Now().UTC()))
require.Equal(t, month2Clients[2], thisMonth.clients[5])
require.Error(t, m.addRepeatedClients(0, &generation.Client{Count: 1, RepeatedFromMonth: 2, Namespace: "other_ns"}, "other_mount", nil))
require.Error(t, m.addRepeatedClients(0, &generation.Client{Count: 1, RepeatedFromMonth: 2, Namespace: "other_ns"}, "other_mount", nil, time.Now().UTC()))
}
// Test_singleMonthActivityClients_populateSegments calls populateSegments for a