Skip to content

[apiserver] ListAllServices with pagination#3490

Merged
kevin85421 merged 21 commits intoray-project:masterfrom
tinaxfwu:feature/pagination-list-all-services
May 9, 2025
Merged

[apiserver] ListAllServices with pagination#3490
kevin85421 merged 21 commits intoray-project:masterfrom
tinaxfwu:feature/pagination-list-all-services

Conversation

@tinaxfwu
Copy link
Contributor

@tinaxfwu tinaxfwu commented Apr 26, 2025

Why are these changes needed?

This PR implements pagination for listing all ray services.

  • delete ListAllServices in resource_manager
  • ListAllRayServices in serve_server reuses ListAllServices in resource_manager
  • An e2e test TestGetAllServicesWithPagination is added to validate the functionality.
  • print service content or size for debugging purpose
~/Doc/G/kuberay/apiserver feature/pagi..all-services *6 !2 ?8 > go test -v ./test/e2e/...  -timeout 60m  -race 
-coverprofile ray-kube-api-server-e2e-coverage.out -count=1 -parallel 4 -run TestGetAllServicesWithPagination
=== RUN   TestGetAllServicesWithPagination
=== RUN   TestGetAllServicesWithPagination/Test_pagination_return_part_of_the_result_services
=== RUN   TestGetAllServicesWithPagination/Test_pagination_return_all_result_services
--- PASS: TestGetAllServicesWithPagination (5.29s)
    --- PASS: TestGetAllServicesWithPagination/Test_pagination_return_part_of_the_result_services (1.19s)
    --- PASS: TestGetAllServicesWithPagination/Test_pagination_return_all_result_services (1.21s)
PASS
coverage: 39.9% of statements
ok      github.com/ray-project/kuberay/apiserver/test/e2e       6.834s  coverage: 39.9% of statements

Related issue number

Closes #3289

Checks

  • I've made sure the tests are passing.
  • Testing Strategy
    • Unit tests
    • Manual tests
    • This PR is not tested :(

@dentiny
Copy link
Contributor

dentiny commented Apr 26, 2025

Feel free to ping me when ready to review :)

}

func createOneServiceInEachNamespace(t *testing.T, numberOfNamespaces int) ([]*End2EndTestingContext, []string) {
tCtxs := make([]*End2EndTestingContext, numberOfNamespaces)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: can we unify the same array creation pattern? I think it's easy to get wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in the latest push.
The array creation pattern is now unified using make([]T, 0, cap)

@tinaxfwu tinaxfwu marked this pull request as ready for review April 27, 2025 20:19
@tinaxfwu tinaxfwu requested a review from dentiny April 27, 2025 20:22
tCtx.DeleteComputeTemplate(t)
})

expectedServiceNames[i] = make([]string, 0, numberOfService)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: as I suggested last time, I would appreciate if we could keep consistency for array usage.

const totalServices = numberOfNamespaces * numberOfService

tCtxs := make([]*End2EndTestingContext, numberOfNamespaces)
expectedServiceNames := make([][]string, numberOfNamespaces)
Copy link
Contributor

Choose a reason for hiding this comment

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

suggest to add some comments for the 2d array, not easy to figure out at first glance

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the two-level for loop and 2d array is a little hard to understand, would it be better if we store an array of a struct

type targetSvc struct {
  namespace string
  svc string
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for your suggestion! I’ve updated it in the latest fix.

t.Run("Test pagination return part of the result services", func(t *testing.T) {
pageToken = ""
gotServices := make([][]bool, numberOfNamespaces)
for i := 0; i < numberOfNamespaces; i++ {
Copy link
Contributor

Choose a reason for hiding this comment

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

suggest to add some comments for the 2d array, not easy to figure out at first glance

if service.Namespace == ctx.GetNamespaceName() &&
service.Name == expectedServiceNames[namespaceIdx][serviceIdx] {
gotServices[namespaceIdx][serviceIdx] = true
break
Copy link
Contributor

Choose a reason for hiding this comment

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

break only breaks out on loop, so even after the target service is found, you still go extra unnecessary for loop (external loop); what about we use a found boolean.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

// Test pagination with limit 1, which is less than the total number of services in all namespaces.
t.Run("Test pagination return part of the result services", func(t *testing.T) {
pageToken = ""
gotServices := make(map[targetService]bool)
Copy link
Contributor

Choose a reason for hiding this comment

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

this is bug, you need to initialize all possibility with false

Copy link
Contributor

Choose a reason for hiding this comment

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

otherwise L300 always gets true

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dentiny Thanks for catching that. It has been fixed and it is ready to be reviewed.

Copy link
Collaborator

@troychiu troychiu left a comment

Choose a reason for hiding this comment

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

LGTM except for 1 nit and @dentiny 's comment

@dentiny
Copy link
Contributor

dentiny commented May 2, 2025

ping me when ready :)

@tinaxfwu tinaxfwu force-pushed the feature/pagination-list-all-services branch from f4d121c to 67981e3 Compare May 2, 2025 22:10
tinaxfwu added 9 commits May 2, 2025 22:15
Signed-off-by: Tina Wu <j6vupz97@gmail.com>
Signed-off-by: Tina Wu <j6vupz97@gmail.com>
Signed-off-by: Tina Wu <j6vupz97@gmail.com>
Signed-off-by: Tina Wu <j6vupz97@gmail.com>
Signed-off-by: Tina Wu <j6vupz97@gmail.com>
Signed-off-by: Tina Wu <j6vupz97@gmail.com>
Signed-off-by: Tina Wu <j6vupz97@gmail.com>
Signed-off-by: Tina Wu <j6vupz97@gmail.com>
Signed-off-by: Tina Wu <j6vupz97@gmail.com>
@tinaxfwu tinaxfwu force-pushed the feature/pagination-list-all-services branch from 67981e3 to 5ca1ca1 Compare May 2, 2025 22:15
tinaxfwu added 2 commits May 2, 2025 22:20
Signed-off-by: Tina <j6vupz97@gmail.com>
require.Nil(t, actualRPCStatus, "No RPC status expected")
require.NotNil(t, response, "A response is expected")
require.NotEmpty(t, response.Services, "A list of service is required")
require.Len(t, response.Services, 1)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: print service content or size for debugging purpose

Copy link
Contributor

@dentiny dentiny left a comment

Choose a reason for hiding this comment

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

Thanks!

}
})

// Test pagination with limit 7, which is larger than the total number of services in all namespaces.
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: I suggest to drop the detailed number (7 here), because it could be changed in the future, which adds maintenance overhead.

require.Empty(t, response.NextPageToken, "Page token should be empty")

for _, service := range response.Services {
gotServices[targetService{
Copy link
Contributor

Choose a reason for hiding this comment

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

sorry there's bug here, you need to check whether the key exist in the gotServices also

}

for _, service := range response.Services {
gotServices[targetService{
Copy link
Contributor

Choose a reason for hiding this comment

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

same here, you need to check service existence

tinaxfwu added 3 commits May 5, 2025 01:20
Signed-off-by: Tina <j6vupz97@gmail.com>
Signed-off-by: Tina <j6vupz97@gmail.com>
Signed-off-by: Tina <j6vupz97@gmail.com>
Signed-off-by: Tina <j6vupz97@gmail.com>
@dentiny
Copy link
Contributor

dentiny commented May 6, 2025

Test failed, could you please take a look? @tinaxfwu

Signed-off-by: Tina Wu <j6vupz97@gmail.com>
@tinaxfwu tinaxfwu requested a review from dentiny May 7, 2025 02:49
// Check all services were found
for _, expectedService := range expectedServices {
if !gotServices[expectedService] {
t.Errorf("ListAllRayServices did not return expected service %s from namespace %s",
Copy link
Contributor

Choose a reason for hiding this comment

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

Two things,

  • You use multiple styles to assert in the test, t.Errorf, t.Fatalf and require, can we unify them into one?
  • t.Errorf doesn't look like a valid assertion

I would suggest use require or assert everywhere

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have replaced t.Errorf and t.Fatalf with require in both TestGetServicesInNamespaceWithPagination and TestGetAllServicesWithPagination.

// Check all services were found
for _, expectedService := range expectedServices {
if !gotServices[expectedService] {
t.Errorf("ListAllRayServices did not return expected service %s from namespace %s",
Copy link
Contributor

Choose a reason for hiding this comment

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

same here

tinaxfwu added 2 commits May 7, 2025 00:18
Signed-off-by: Tina Wu <j6vupz97@gmail.com>
Signed-off-by: Tina Wu <j6vupz97@gmail.com>
@dentiny
Copy link
Contributor

dentiny commented May 7, 2025

Feel free to ping me when ready thanks

Signed-off-by: Tina Wu <j6vupz97@gmail.com>
@tinaxfwu tinaxfwu requested a review from dentiny May 7, 2025 06:26
require.NotNil(t, response, "A response is expected")
require.NotEmpty(t, response.Services, "A list of service is required")
require.Len(t, response.Services, 1)
t.Logf("Got %d services in response, expected %d", len(response.Services), 1)
Copy link
Contributor

Choose a reason for hiding this comment

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

what's the purpose for this logging?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You left a nit comment earlier about
printing service content or size for debugging purpose. So I add this line to log the size of services received.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry for the confusion, I mean log when error

Copy link
Contributor

Choose a reason for hiding this comment

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

T. Require(,, message)

}

for _, service := range response.Services {
t.Logf("Got service: namespace=%s, name=%s", service.Namespace, service.Name)
Copy link
Contributor

Choose a reason for hiding this comment

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

same here, what's the purpose of logging?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have removed this line, as the information it printed might be too trivial.

require.NotEmpty(t, response.Services, "A list of services is required")
require.Len(t, response.Services, totalServices)
require.Empty(t, response.NextPageToken, "Page token should be empty")
t.Logf("Got %d services in response, expected %d", len(response.Services), totalServices)
Copy link
Contributor

Choose a reason for hiding this comment

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

same question here

Copy link
Contributor

Choose a reason for hiding this comment

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

@tinaxfwu should this one be removed as well?

tinaxfwu added 2 commits May 8, 2025 08:10
Signed-off-by: Tina Wu <j6vupz97@gmail.com>
Signed-off-by: Tina Wu <j6vupz97@gmail.com>
@tinaxfwu tinaxfwu requested a review from dentiny May 9, 2025 03:19
Copy link
Contributor

@dentiny dentiny left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for the effort!

@kevin85421 kevin85421 merged commit 0721d8f into ray-project:master May 9, 2025
24 checks passed
@tinaxfwu tinaxfwu deleted the feature/pagination-list-all-services branch May 9, 2025 20:47
pawelpaszki pushed a commit to opendatahub-io/kuberay that referenced this pull request May 26, 2025
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.

[apiserver][feat] add pagination to ListAllServices

4 participants