-
Notifications
You must be signed in to change notification settings - Fork 1
Add proper server-side query support for instances #26
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR implements proper server-side query support for Redis instances, replacing the previous client-side filtering approach with RedisVL queries. This follows the migration from serialized JSON strings to Redis hashes (RAAE-1176), now leveraging RediSearch capabilities for efficient filtering and pagination.
Key Changes:
- Implemented
query_instances()function with support for filtering by environment, usage, status, instance_type, user_id, and text search on instance names - Added pagination support with limit/offset parameters and total count tracking
- Updated all API endpoints and UI components to use the new query-based approach with structured response format
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
redis_sre_agent/core/instances.py |
Added query_instances() with RedisVL filter expressions and pagination; refactored get_instance_by_id() and get_instance_by_name() to use direct lookups |
redis_sre_agent/api/instances.py |
Updated /instances endpoint to accept query parameters and return paginated response with InstanceListResponse model |
redis_sre_agent/mcp_server/server.py |
Enhanced redis_sre_list_instances() tool with filter parameters and updated documentation |
ui/src/services/sreAgentApi.ts |
Added ListInstancesParams and InstanceListResponse interfaces; updated listInstances() to build query parameters from filter options |
ui/src/pages/Instances.tsx |
Refactored to use server-side filtering with buildFilterParams() and handle paginated response structure |
ui/src/pages/Dashboard.tsx |
Updated to access response.instances from new response structure |
ui/src/pages/Triage.tsx |
Updated to access response.instances from new response structure |
ui/src/pages/Schedules.tsx |
Updated to access response.instances from new response structure |
tests/unit/api/test_instances_api.py |
Updated tests to mock query_instances() instead of get_instances() and added test for filter parameters |
tests/unit/mcp_server/test_mcp_server.py |
Updated tests to use InstanceQueryResult and added test coverage for filter parameters |
Comments suppressed due to low confidence (1)
ui/src/pages/Instances.tsx:1037
- The client-side filtering is redundant and adds unnecessary complexity. Since server-side filtering is now implemented, consider removing the client-side filter logic. If you need it as a "safety net," this suggests a lack of confidence in the server-side implementation and could mask bugs. The comment also indicates that in a race condition, the client might show incorrect results (stale filtered data before the new server response arrives).
// Filtering is now done server-side, but we keep client-side filtering
// as a safety net in case filters change before the API response arrives.
// In most cases, instances will already be filtered correctly from the server.
const filteredInstances = instances.filter((instance) => {
const environmentMatch =
selectedEnvironment === "all" ||
instance.environment === selectedEnvironment;
const usageMatch =
selectedUsage === "all" || instance.usage === selectedUsage;
return environmentMatch && usageMatch;
});
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| description="Filter by type (oss_single, oss_cluster, redis_enterprise, redis_cloud)", | ||
| ), | ||
| user_id: Optional[str] = Query(None, description="Filter by user ID"), | ||
| search: Optional[str] = Query(None, description="Search by instance name"), |
Copilot
AI
Dec 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new search parameter is not covered by tests. Consider adding test cases for the search functionality to verify text search behavior works as expected, especially for partial matches and edge cases like empty strings or special characters.
redis_sre_agent/core/instances.py
Outdated
| # Note: name is indexed as TEXT, so we search for the exact term | ||
| fq = FilterQuery( | ||
| return_fields=["data"], | ||
| num_results=1, | ||
| ) | ||
| fq.set_filter(Text("name") % instance_name) |
Copilot
AI
Dec 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment states "Use exact text match on name field" but the implementation at line 796 uses the % operator which performs fuzzy text search in RediSearch, not exact matching. This comment is misleading and should be corrected to reflect the actual behavior (fuzzy/prefix matching) or the implementation should be changed to perform exact matching.
| # Note: name is indexed as TEXT, so we search for the exact term | |
| fq = FilterQuery( | |
| return_fields=["data"], | |
| num_results=1, | |
| ) | |
| fq.set_filter(Text("name") % instance_name) | |
| # Note: name is indexed as TEXT, so we search for the exact term using == | |
| fq = FilterQuery( | |
| return_fields=["data"], | |
| num_results=1, | |
| ) | |
| fq.set_filter(Text("name") == instance_name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this should be a TAG field with a TAG filter.
redis_sre_agent/core/instances.py
Outdated
| return_fields=["data"], | ||
| num_results=1, | ||
| ) | ||
| fq.set_filter(Text("name") % instance_name) |
Copilot
AI
Dec 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The text search implementation is inconsistent between search parameter (line 418) and get_instance_by_name. The search parameter uses wildcard matching with Text("name") % f"*{search}*" for partial matches, but get_instance_by_name uses Text("name") % instance_name without wildcards. This means get_instance_by_name will perform a partial match search when it should do an exact match lookup. Consider using Tag field for exact name matching or explicitly handle exact vs partial matching differently.
| fq.set_filter(Text("name") % instance_name) | |
| fq.set_filter(Text("name") % f'"{instance_name}"') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instance_name should be a TAG field with a TAG filter here
redis_sre_agent/core/instances.py
Outdated
| # Text search on name field (supports prefix matching) | ||
| name_filter = Text("name") % f"*{search}*" |
Copilot
AI
Dec 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The wildcard pattern f"*{search}*" is being passed to Text field search. In RediSearch, this pattern syntax may not work as intended for TEXT fields. Text fields typically use word-based tokenization and prefix matching. Consider using proper RediSearch query syntax for fuzzy/wildcard text searches, or ensure this pattern is compatible with how the name field is indexed.
| # Text search on name field (supports prefix matching) | |
| name_filter = Text("name") % f"*{search}*" | |
| # Text search on name field (supports prefix matching only) | |
| name_filter = Text("name") % f"{search}*" |
| fq = FilterQuery( | ||
| return_fields=["data"], | ||
| num_results=limit, | ||
| ).sort_by("updated_at", asc=False) | ||
|
|
||
| if filter_expr is not None: | ||
| fq.set_filter(filter_expr) | ||
|
|
||
| fq.paging(offset, limit) |
Copilot
AI
Dec 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The paging call uses positional arguments fq.paging(offset, limit), but this appears to be called after setting num_results. This could lead to confusion about which parameter controls pagination. Verify that paging() correctly overrides the num_results set on line 439, or consider removing the redundant num_results parameter from the FilterQuery constructor.
ui/src/pages/Instances.tsx
Outdated
|
|
||
| const Instances = () => { | ||
| const [instances, setInstances] = useState<RedisInstance[]>([]); | ||
| const [totalCount, setTotalCount] = useState(0); |
Copilot
AI
Dec 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable totalCount.
| const [totalCount, setTotalCount] = useState(0); |
redis_sre_agent/core/instances.py
Outdated
| from datetime import datetime, timezone | ||
| from enum import Enum | ||
| from typing import Any, Dict, List, Optional | ||
| from typing import Any, Dict, List, Optional, Tuple |
Copilot
AI
Dec 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import of 'Tuple' is not used.
| from typing import Any, Dict, List, Optional, Tuple | |
| from typing import Any, Dict, List, Optional |
In RAAE-1176, we moved instance data from serialized JSON in strings (yikes) to hashes. However, we left all the querying logic in place... which was basically "filter this list of dictionaries." This PR exposes more query abilities related to instances and uses proper RedisVL queries to serve them.