Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Access] Response limit tracker #6814

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

Guitarheroua
Copy link
Contributor

Closes: #6640

NOTE: this PR should be merged after #6798

Implements rate limiter for responses, that controller returns to client.

@codecov-commenter
Copy link

codecov-commenter commented Dec 13, 2024

Codecov Report

Attention: Patch coverage is 25.00000% with 3 lines in your changes missing coverage. Please review.

Project coverage is 41.19%. Comparing base (72adf9e) to head (4c0c2f8).

Files with missing lines Patch % Lines
engine/access/rest/websockets/controller.go 25.00% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6814       /-   ##
==========================================
  Coverage   41.18%   41.19%    0.01%     
==========================================
  Files        2109     2109              
  Lines      185660   185664        4     
==========================================
  Hits        76460    76489       29     
  Misses     102788   102766      -22     
  Partials     6412     6409       -3     
Flag Coverage Δ
unittests 41.19% <25.00%> ( 0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@illia-malachyn illia-malachyn changed the title [Access] Responce limit tracker [Access] Response limit tracker Dec 13, 2024
@Guitarheroua Guitarheroua self-assigned this Dec 17, 2024
@peterargue
Copy link
Contributor

@Guitarheroua please address merge conflicts

@Guitarheroua Guitarheroua marked this pull request as ready for review December 30, 2024 14:10
@illia-malachyn illia-malachyn self-requested a review January 2, 2025 10:43
@peterargue peterargue requested a review from durkmurder January 2, 2025 17:56
Comment on lines 684 to 728
s.T().Run("Enforces response rate limit", func(t *testing.T) {
totalMessages := 5 // Number of messages to simulate.

// Step 1: Create a mock WebSocket connection.
conn := connmock.NewWebsocketConnection(t)
conn.On("SetWriteDeadline", mock.Anything).Return(nil).Times(totalMessages)

// Step 2: Configure the WebSocket controller with a rate limit.
config := NewDefaultWebsocketConfig()
config.MaxResponsesPerSecond = 2 // 2 messages per second.
controller := NewWebSocketController(s.logger, config, conn, nil)

// Step 3: Simulate sending messages to the controller's `multiplexedStream`.
go func() {
for i := 0; i < totalMessages; i {
controller.multiplexedStream <- map[string]interface{}{
"message": i,
}
}
close(controller.multiplexedStream)
}()

// Step 4: Collect timestamps of message writes for verification.
var timestamps []time.Time
conn.On("WriteJSON", mock.Anything).Run(func(args mock.Arguments) {
timestamps = append(timestamps, time.Now())
}).Return(nil).Times(totalMessages)

// Invoke the `writeMessages` method to process the stream.
_ = controller.writeMessages(context.Background())

// Step 5: Verify that all messages are processed.
require.Len(t, timestamps, totalMessages, "All messages should be processed")

// Calculate the expected delay between messages based on the rate limit.
expectedDelay := time.Second / time.Duration(config.MaxResponsesPerSecond)
const tolerance = 5 * time.Millisecond // Allow up to 5ms deviation.

// Step 6: Assert that the delays respect the rate limit with tolerance.
for i := 1; i < len(timestamps); i {
delay := timestamps[i].Sub(timestamps[i-1])
assert.GreaterOrEqual(t, delay, expectedDelay-tolerance, "Messages should respect the minimum rate limit")
assert.LessOrEqual(t, delay, expectedDelay tolerance, "Messages should respect the maximum rate limit")
}
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
s.T().Run("Enforces response rate limit", func(t *testing.T) {
totalMessages := 5 // Number of messages to simulate.
// Step 1: Create a mock WebSocket connection.
conn := connmock.NewWebsocketConnection(t)
conn.On("SetWriteDeadline", mock.Anything).Return(nil).Times(totalMessages)
// Step 2: Configure the WebSocket controller with a rate limit.
config := NewDefaultWebsocketConfig()
config.MaxResponsesPerSecond = 2 // 2 messages per second.
controller := NewWebSocketController(s.logger, config, conn, nil)
// Step 3: Simulate sending messages to the controller's `multiplexedStream`.
go func() {
for i := 0; i < totalMessages; i {
controller.multiplexedStream <- map[string]interface{}{
"message": i,
}
}
close(controller.multiplexedStream)
}()
// Step 4: Collect timestamps of message writes for verification.
var timestamps []time.Time
conn.On("WriteJSON", mock.Anything).Run(func(args mock.Arguments) {
timestamps = append(timestamps, time.Now())
}).Return(nil).Times(totalMessages)
// Invoke the `writeMessages` method to process the stream.
_ = controller.writeMessages(context.Background())
// Step 5: Verify that all messages are processed.
require.Len(t, timestamps, totalMessages, "All messages should be processed")
// Calculate the expected delay between messages based on the rate limit.
expectedDelay := time.Second / time.Duration(config.MaxResponsesPerSecond)
const tolerance = 5 * time.Millisecond // Allow up to 5ms deviation.
// Step 6: Assert that the delays respect the rate limit with tolerance.
for i := 1; i < len(timestamps); i {
delay := timestamps[i].Sub(timestamps[i-1])
assert.GreaterOrEqual(t, delay, expectedDelay-tolerance, "Messages should respect the minimum rate limit")
assert.LessOrEqual(t, delay, expectedDelay tolerance, "Messages should respect the maximum rate limit")
}
})
totalMessages := 5 // Number of messages to simulate.
// Step 1: Create a mock WebSocket connection.
conn := connmock.NewWebsocketConnection(t)
conn.On("SetWriteDeadline", mock.Anything).Return(nil).Times(totalMessages)
// Step 2: Configure the WebSocket controller with a rate limit.
config := NewDefaultWebsocketConfig()
config.MaxResponsesPerSecond = 2 // 2 messages per second.
controller := NewWebSocketController(s.logger, config, conn, nil)
// Step 3: Simulate sending messages to the controller's `multiplexedStream`.
go func() {
for i := 0; i < totalMessages; i {
controller.multiplexedStream <- map[string]interface{}{
"message": i,
}
}
close(controller.multiplexedStream)
}()
// Step 4: Collect timestamps of message writes for verification.
var timestamps []time.Time
conn.On("WriteJSON", mock.Anything).Run(func(args mock.Arguments) {
timestamps = append(timestamps, time.Now())
}).Return(nil).Times(totalMessages)
// Invoke the `writeMessages` method to process the stream.
_ = controller.writeMessages(context.Background())
// Step 5: Verify that all messages are processed.
require.Len(t, timestamps, totalMessages, "All messages should be processed")
// Calculate the expected delay between messages based on the rate limit.
expectedDelay := time.Second / time.Duration(config.MaxResponsesPerSecond)
const tolerance = 5 * time.Millisecond // Allow up to 5ms deviation.
// Step 6: Assert that the delays respect the rate limit with tolerance.
for i := 1; i < len(timestamps); i {
delay := timestamps[i].Sub(timestamps[i-1])
assert.GreaterOrEqual(t, delay, expectedDelay-tolerance, "Messages should respect the minimum rate limit")
assert.LessOrEqual(t, delay, expectedDelay tolerance, "Messages should respect the maximum rate limit")
}

// Step 4: Collect timestamps of message writes for verification.
var timestamps []time.Time
conn.On("WriteJSON", mock.Anything).Run(func(args mock.Arguments) {
timestamps = append(timestamps, time.Now())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets also check that we indeed receive expected messages in expected order at this place.

// Step 6: Assert that the delays respect the rate limit with tolerance.
for i := 1; i < len(timestamps); i {
delay := timestamps[i].Sub(timestamps[i-1])
assert.GreaterOrEqual(t, delay, expectedDelay-tolerance, "Messages should respect the minimum rate limit")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could possibly simplify next statement using: https://github.com/stretchr/testify/blob/master/require/require.go#L869

Copy link
Member

@durkmurder durkmurder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Left a few comments tests related.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Access] Implement response limit tracker routine for ws controller
5 participants