Skip to content

Commit 3a874a9

Browse files
panjwanirahulcursoragent
authored andcommitted
Avoid null cache writes for query routing mappings
Guard query-id cache writes in routing manager against null/empty values to prevent runtime failures when query history rows have been removed by retention. - Use direct static import for isNullOrEmpty. - Change log level from warn to debug for empty queryId checks. - Standardize null/empty handling across all cache setters. - Add regression tests for null/empty external URL and query ID. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent e516441 commit 3a874a9

2 files changed

Lines changed: 79 additions & 5 deletions

File tree

gateway-ha/src/main/java/io/trino/gateway/ha/router/BaseRoutingManager.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import com.github.benmanes.caffeine.cache.LoadingCache;
1818
import com.google.common.annotations.VisibleForTesting;
1919
import com.google.common.base.Function;
20-
import com.google.common.base.Strings;
2120
import io.airlift.log.Logger;
2221
import io.trino.gateway.ha.clustermonitor.ClusterStats;
2322
import io.trino.gateway.ha.clustermonitor.TrinoStatus;
@@ -40,6 +39,8 @@
4039
import java.util.concurrent.Future;
4140
import java.util.concurrent.TimeUnit;
4241

42+
import static com.google.common.base.Strings.isNullOrEmpty;
43+
4344
/**
4445
* This class performs health check, stats counts for each backend and provides a backend given
4546
* request object. Default implementation comes here.
@@ -76,12 +77,30 @@ public BaseRoutingManager(GatewayBackendManager gatewayBackendManager, QueryHist
7677
@Override
7778
public void setBackendForQueryId(String queryId, String backend)
7879
{
80+
if (isNullOrEmpty(queryId)) {
81+
log.debug("Skipping backend cache update for empty queryId");
82+
return;
83+
}
84+
if (isNullOrEmpty(backend)) {
85+
queryIdBackendCache.invalidate(queryId);
86+
log.debug("Invalidated backend cache for queryId [%s] due to empty backend", queryId);
87+
return;
88+
}
7989
queryIdBackendCache.put(queryId, backend);
8090
}
8191

8292
@Override
8393
public void setRoutingGroupForQueryId(String queryId, String routingGroup)
8494
{
95+
if (isNullOrEmpty(queryId)) {
96+
log.debug("Skipping routing group cache update for empty queryId");
97+
return;
98+
}
99+
if (isNullOrEmpty(routingGroup)) {
100+
queryIdRoutingGroupCache.invalidate(queryId);
101+
log.debug("Invalidated routing group cache for queryId [%s] due to empty routing group", queryId);
102+
return;
103+
}
85104
queryIdRoutingGroupCache.put(queryId, routingGroup);
86105
}
87106

@@ -177,6 +196,15 @@ public void updateClusterStats(List<ClusterStats> stats)
177196
@Override
178197
public void setExternalUrlForQueryId(String queryId, String externalUrl)
179198
{
199+
if (isNullOrEmpty(queryId)) {
200+
log.debug("Skipping externalUrl cache update for empty queryId");
201+
return;
202+
}
203+
if (isNullOrEmpty(externalUrl)) {
204+
queryIdExternalUrlCache.invalidate(queryId);
205+
log.debug("Invalidated externalUrl cache for queryId [%s] due to empty externalUrl", queryId);
206+
return;
207+
}
180208
queryIdExternalUrlCache.put(queryId, externalUrl);
181209
}
182210

@@ -185,7 +213,7 @@ String findBackendForUnknownQueryId(String queryId)
185213
{
186214
String backend;
187215
backend = queryHistoryManager.getBackendForQueryId(queryId);
188-
if (Strings.isNullOrEmpty(backend)) {
216+
if (isNullOrEmpty(backend)) {
189217
log.debug("Unable to find backend mapping for [%s]. Searching for suitable backend", queryId);
190218
backend = searchAllBackendForQuery(queryId);
191219
}

gateway-ha/src/test/java/io/trino/gateway/ha/router/TestRoutingManagerExternalUrlCache.java

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.mockito.Mockito;
2222

2323
import static org.assertj.core.api.Assertions.assertThat;
24+
import static org.assertj.core.api.Assertions.assertThatCode;
2425
import static org.mockito.Mockito.when;
2526

2627
@TestInstance(Lifecycle.PER_CLASS)
@@ -106,11 +107,56 @@ void testMultipleQueryIdsWithDifferentExternalUrls()
106107
void testEmptyStringExternalUrl()
107108
{
108109
String queryId = "empty-url-test";
109-
String emptyUrl = "";
110-
routingManager.setExternalUrlForQueryId(queryId, emptyUrl);
110+
String expectedFallbackUrl = "https://fallback-after-empty.example.com";
111+
112+
when(queryHistoryManager.getExternalUrlForQueryId(queryId)).thenReturn(expectedFallbackUrl);
113+
114+
routingManager.setExternalUrlForQueryId(queryId, "");
115+
String retrievedUrl = routingManager.findExternalUrlForQueryId(queryId);
116+
117+
assertThat(retrievedUrl).isEqualTo(expectedFallbackUrl);
118+
Mockito.verify(queryHistoryManager).getExternalUrlForQueryId(queryId);
119+
}
120+
121+
@Test
122+
void testNullExternalUrlDoesNotThrowAndFallsBackToQueryHistory()
123+
{
124+
String queryId = "null-external-url-query";
125+
String expectedFallbackUrl = "https://fallback-after-null.example.com";
126+
127+
when(queryHistoryManager.getExternalUrlForQueryId(queryId)).thenReturn(expectedFallbackUrl);
128+
129+
assertThatCode(() -> routingManager.setExternalUrlForQueryId(queryId, null))
130+
.doesNotThrowAnyException();
131+
111132
String retrievedUrl = routingManager.findExternalUrlForQueryId(queryId);
112133

113-
assertThat(retrievedUrl).isEqualTo(emptyUrl);
134+
assertThat(retrievedUrl).isEqualTo(expectedFallbackUrl);
135+
Mockito.verify(queryHistoryManager).getExternalUrlForQueryId(queryId);
136+
}
137+
138+
@Test
139+
void testEmptyExternalUrlDoesNotThrowAndFallsBackToQueryHistory()
140+
{
141+
String queryId = "empty-external-url-query";
142+
String expectedFallbackUrl = "https://fallback-after-empty.example.com";
143+
144+
when(queryHistoryManager.getExternalUrlForQueryId(queryId)).thenReturn(expectedFallbackUrl);
145+
146+
assertThatCode(() -> routingManager.setExternalUrlForQueryId(queryId, ""))
147+
.doesNotThrowAnyException();
148+
149+
String retrievedUrl = routingManager.findExternalUrlForQueryId(queryId);
150+
151+
assertThat(retrievedUrl).isEqualTo(expectedFallbackUrl);
152+
Mockito.verify(queryHistoryManager).getExternalUrlForQueryId(queryId);
153+
}
154+
155+
@Test
156+
void testNullQueryIdDoesNotThrowForExternalUrlCacheSet()
157+
{
158+
assertThatCode(() -> routingManager.setExternalUrlForQueryId(null, "https://external-url.example.com"))
159+
.doesNotThrowAnyException();
114160
}
115161

116162
@Test

0 commit comments

Comments
 (0)