Skip to content

Commit bcb9b08

Browse files
authored
branch-4.0: Revert "[Enhancement](sql-dialect) Support multiple sql-converter service urls (#52636)" (#59611)
bp #59610
1 parent 82d28ef commit bcb9b08

File tree

3 files changed

+38
-599
lines changed

3 files changed

+38
-599
lines changed

fe/fe-core/src/main/java/org/apache/doris/plugin/dialect/HttpDialectConverterPlugin.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,15 @@ public ImmutableSet<Dialect> acceptDialects() {
103103
if (Strings.isNullOrEmpty(targetURL)) {
104104
return null;
105105
}
106+
// TODO: support multiple URLs load balancing, here we just use the first one
107+
String[] urlArray = targetURL.split(",");
108+
for (String url : urlArray) {
109+
String trimmedUrl = url.trim();
110+
if (!trimmedUrl.isEmpty()) {
111+
targetURL = trimmedUrl;
112+
break;
113+
}
114+
}
106115
return HttpDialectUtils.convertSql(targetURL, originSql, sessionVariable.getSqlDialect(),
107116
sessionVariable.getSqlConvertorFeatures(), sessionVariable.getSqlConvertorConfig());
108117
}

fe/fe-core/src/main/java/org/apache/doris/plugin/dialect/HttpDialectUtils.java

Lines changed: 14 additions & 254 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@
1717

1818
package org.apache.doris.plugin.dialect;
1919

20-
import com.github.benmanes.caffeine.cache.Cache;
21-
import com.github.benmanes.caffeine.cache.Caffeine;
22-
import com.google.common.collect.Lists;
2320
import com.google.gson.Gson;
2421
import com.google.gson.reflect.TypeToken;
2522
import lombok.Data;
@@ -33,113 +30,35 @@
3330
import java.net.HttpURLConnection;
3431
import java.net.URL;
3532
import java.nio.charset.StandardCharsets;
36-
import java.util.Collections;
37-
import java.util.Date;
38-
import java.util.List;
39-
import java.util.concurrent.ConcurrentHashMap;
40-
import java.util.concurrent.ThreadLocalRandom;
41-
import java.util.concurrent.TimeUnit;
4233

4334
/**
4435
* This class is used to convert sql with different dialects using sql convertor service.
4536
* The sql convertor service is a http service which is used to convert sql.
46-
* <p>
47-
* Features:
48-
* - Support multiple URLs (comma separated)
49-
* - Blacklist mechanism for failed URLs
50-
* - Automatic failover and retry
51-
* - URL caching and smart selection
5237
*/
5338
public class HttpDialectUtils {
5439
private static final Logger LOG = LogManager.getLogger(HttpDialectUtils.class);
5540

56-
// Cache URL manager instances to avoid duplicate parsing with automatic expiration
57-
private static final Cache<String, UrlManager> urlManagerCache = Caffeine.newBuilder()
58-
.maximumSize(10)
59-
.expireAfterAccess(8, TimeUnit.HOURS)
60-
.build();
61-
62-
// Blacklist recovery time (ms): 1 minute
63-
private static final long BLACKLIST_RECOVERY_TIME_MS = 60 * 1000;
64-
// Connection timeout period (ms): 3 seconds
65-
private static final int CONNECTION_TIMEOUT_MS = 3000;
66-
// Read timeout period (ms): 10 seconds
67-
private static final int READ_TIMEOUT_MS = 10000;
68-
69-
public static String convertSql(String targetURLs, String originStmt, String dialect,
41+
public static String convertSql(String targetURL, String originStmt, String dialect,
7042
String[] features, String config) {
71-
UrlManager urlManager = getOrCreateUrlManager(targetURLs);
7243
ConvertRequest convertRequest = new ConvertRequest(originStmt, dialect, features, config);
73-
String requestStr = convertRequest.toJson();
74-
75-
// Try to convert SQL using intelligent URL selection strategy
76-
return tryConvertWithIntelligentSelection(urlManager, requestStr, originStmt);
77-
}
78-
79-
/**
80-
* Try to convert SQL using intelligent URL selection strategy
81-
* CRITICAL: This method ensures 100% success rate when ANY service is available
82-
*/
83-
private static String tryConvertWithIntelligentSelection(
84-
UrlManager urlManager, String requestStr, String originStmt) {
85-
// Strategy: Try ALL URLs in intelligent order, regardless of blacklist status
86-
// This ensures 100% success rate when any service is actually available
8744

88-
List<String> allUrls = urlManager.getAllUrlsInPriorityOrder();
89-
90-
for (String url : allUrls) {
91-
try {
92-
String result = doConvertSql(url, requestStr);
93-
// If no exception thrown, HTTP response was successful (200)
94-
// Mark URL as healthy and return result (even if empty)
95-
urlManager.markUrlAsHealthy(url);
96-
if (LOG.isDebugEnabled()) {
97-
LOG.debug("Successfully converted SQL using URL: {}", url);
98-
}
99-
return result;
100-
} catch (Exception e) {
101-
LOG.warn("Failed to convert SQL using URL: {}, error: {}", url, e.getMessage());
102-
// Add failed URL to blacklist for future optimization
103-
urlManager.markUrlAsBlacklisted(url);
104-
// Continue trying next URL - this is CRITICAL for 100% success rate
105-
}
106-
}
107-
108-
return originStmt;
109-
}
110-
111-
/**
112-
* Get or create a URL manager
113-
*/
114-
private static UrlManager getOrCreateUrlManager(String targetURLs) {
115-
return urlManagerCache.get(targetURLs, UrlManager::new);
116-
}
117-
118-
/**
119-
* Perform SQL conversion for individual URL
120-
*/
121-
private static String doConvertSql(String targetURL, String requestStr) throws Exception {
12245
HttpURLConnection connection = null;
12346
try {
124-
if (targetURL == null || targetURL.trim().isEmpty()) {
125-
throw new Exception("Target URL is null or empty");
126-
}
127-
URL url = new URL(targetURL.trim());
47+
URL url = new URL(targetURL);
12848
connection = (HttpURLConnection) url.openConnection();
12949
connection.setRequestMethod("POST");
13050
connection.setRequestProperty("Content-Type", "application/json");
13151
connection.setUseCaches(false);
13252
connection.setDoOutput(true);
133-
connection.setConnectTimeout(CONNECTION_TIMEOUT_MS);
134-
connection.setReadTimeout(READ_TIMEOUT_MS);
13553

54+
String requestStr = convertRequest.toJson();
13655
try (OutputStream outputStream = connection.getOutputStream()) {
13756
outputStream.write(requestStr.getBytes(StandardCharsets.UTF_8));
13857
}
13958

14059
int responseCode = connection.getResponseCode();
14160
if (LOG.isDebugEnabled()) {
142-
LOG.debug("POST Response Code: {}, URL: {}, post data: {}", responseCode, targetURL, requestStr);
61+
LOG.debug("POST Response Code: {}, post data: {}", responseCode, requestStr);
14362
}
14463

14564
if (responseCode == HttpURLConnection.HTTP_OK) {
@@ -157,192 +76,33 @@ private static String doConvertSql(String targetURL, String requestStr) throws E
15776
}.getType();
15877
ConvertResponse result = new Gson().fromJson(response.toString(), type);
15978
if (LOG.isDebugEnabled()) {
160-
LOG.debug("Convert response: {}, URL: {}", result, targetURL);
79+
LOG.debug("convert response: {}", result);
16180
}
16281
if (result.code == 0) {
16382
if (!"v1".equals(result.version)) {
164-
throw new Exception("Unsupported version: " + result.version);
83+
LOG.warn("failed to convert sql, response version is not v1: {}", result.version);
84+
return originStmt;
16585
}
16686
return result.data;
16787
} else {
168-
throw new Exception("Conversion failed: " + result.message);
88+
LOG.warn("failed to convert sql, response: {}", result);
89+
return originStmt;
16990
}
17091
}
17192
} else {
172-
throw new Exception("HTTP response code: " + responseCode);
93+
LOG.warn("failed to convert sql, response code: {}", responseCode);
94+
return originStmt;
17395
}
96+
} catch (Exception e) {
97+
LOG.warn("failed to convert sql", e);
98+
return originStmt;
17499
} finally {
175100
if (connection != null) {
176101
connection.disconnect();
177102
}
178103
}
179104
}
180105

181-
/**
182-
* URL Manager - Responsible for URL parsing, caching, blacklist management, and smart selection
183-
*/
184-
private static class UrlManager {
185-
private final List<String> parsedUrls;
186-
private final ConcurrentHashMap<String, BlacklistEntry> blacklist;
187-
188-
public UrlManager(String urls) {
189-
this.parsedUrls = parseUrls(urls);
190-
this.blacklist = new ConcurrentHashMap<>();
191-
if (LOG.isDebugEnabled()) {
192-
LOG.debug("Created UrlManager with URLs: {}, parsed: {}", urls, parsedUrls);
193-
}
194-
}
195-
196-
/**
197-
* Parse comma separated URL strings
198-
*/
199-
private List<String> parseUrls(String urls) {
200-
List<String> result = Lists.newArrayList();
201-
if (urls != null && !urls.trim().isEmpty()) {
202-
String[] urlArray = urls.split(",");
203-
for (String url : urlArray) {
204-
String trimmedUrl = url.trim();
205-
if (!trimmedUrl.isEmpty()) {
206-
result.add(trimmedUrl);
207-
}
208-
}
209-
}
210-
return result;
211-
}
212-
213-
/**
214-
* Mark URL as healthy (remove from blacklist)
215-
*/
216-
public void markUrlAsHealthy(String url) {
217-
if (blacklist.remove(url) != null) {
218-
LOG.info("Removed URL from blacklist due to successful request: {}", url);
219-
}
220-
}
221-
222-
/**
223-
* Add URL to blacklist
224-
*/
225-
public void markUrlAsBlacklisted(String url) {
226-
// If URL is already in blacklist, just return
227-
if (blacklist.containsKey(url)) {
228-
return;
229-
}
230-
231-
long currentTime = System.currentTimeMillis();
232-
long recoverTime = currentTime + BLACKLIST_RECOVERY_TIME_MS;
233-
blacklist.put(url, new BlacklistEntry(currentTime, recoverTime));
234-
LOG.warn("Added URL to blacklist: {}, will recover at: {}", url, new Date(recoverTime));
235-
}
236-
237-
/**
238-
* Check if URL is localhost (127.0.0.1 or localhost)
239-
*/
240-
private boolean isLocalhost(String url) {
241-
return url.contains("127.0.0.1") || url.contains("localhost");
242-
}
243-
244-
/**
245-
* Get ALL URLs in priority order for 100% success guarantee
246-
* CRITICAL: This method ensures we try every URL when any service might be available
247-
* <p>
248-
* Priority order:
249-
* 1. Localhost URLs (127.0.0.1 or localhost) that are healthy
250-
* 2. Other healthy URLs (randomly selected)
251-
* 3. Localhost URLs in blacklist
252-
* 4. Other blacklisted URLs (sorted by recovery time)
253-
*/
254-
public List<String> getAllUrlsInPriorityOrder() {
255-
List<String> prioritizedUrls = Lists.newArrayList();
256-
List<String> healthyLocalhost = Lists.newArrayList();
257-
List<String> healthyOthers = Lists.newArrayList();
258-
List<String> blacklistedLocalhost = Lists.newArrayList();
259-
List<String> blacklistedOthers = Lists.newArrayList();
260-
261-
long currentTime = System.currentTimeMillis();
262-
263-
// Single traversal to categorize all URLs
264-
for (String url : parsedUrls) {
265-
BlacklistEntry entry = blacklist.get(url);
266-
boolean isHealthy = false;
267-
268-
if (entry == null) {
269-
// URL is not in blacklist, consider it healthy
270-
isHealthy = true;
271-
} else if (currentTime >= entry.recoverTime) {
272-
// URL has reached recovery time, remove from blacklist and consider healthy
273-
blacklist.remove(url);
274-
isHealthy = true;
275-
if (LOG.isDebugEnabled()) {
276-
LOG.debug("URL recovered from blacklist: {}", url);
277-
}
278-
}
279-
280-
boolean isLocal = isLocalhost(url);
281-
282-
if (isHealthy) {
283-
if (isLocal) {
284-
healthyLocalhost.add(url);
285-
} else {
286-
healthyOthers.add(url);
287-
}
288-
} else {
289-
if (isLocal) {
290-
blacklistedLocalhost.add(url);
291-
} else {
292-
blacklistedOthers.add(url);
293-
}
294-
}
295-
}
296-
297-
// Add URLs in priority order
298-
// 1. Healthy localhost URLs first
299-
prioritizedUrls.addAll(healthyLocalhost);
300-
301-
// 2. Other healthy URLs (randomly shuffled for load balancing)
302-
Collections.shuffle(healthyOthers, ThreadLocalRandom.current());
303-
prioritizedUrls.addAll(healthyOthers);
304-
305-
// 3. Blacklisted localhost URLs
306-
prioritizedUrls.addAll(blacklistedLocalhost);
307-
308-
// 4. Other blacklisted URLs (sorted by recovery time)
309-
blacklistedOthers.sort((url1, url2) -> {
310-
BlacklistEntry entry1 = blacklist.get(url1);
311-
BlacklistEntry entry2 = blacklist.get(url2);
312-
if (entry1 == null && entry2 == null) {
313-
return 0;
314-
}
315-
if (entry1 == null) {
316-
return -1;
317-
}
318-
if (entry2 == null) {
319-
return 1;
320-
}
321-
return Long.compare(entry1.recoverTime, entry2.recoverTime);
322-
});
323-
prioritizedUrls.addAll(blacklistedOthers);
324-
325-
if (LOG.isDebugEnabled()) {
326-
LOG.debug("All URLs in priority order: {}", prioritizedUrls);
327-
}
328-
329-
return prioritizedUrls;
330-
}
331-
}
332-
333-
/**
334-
* Blacklist entry
335-
*/
336-
private static class BlacklistEntry {
337-
final long blacklistedTime;
338-
final long recoverTime;
339-
340-
BlacklistEntry(long blacklistedTime, long recoverTime) {
341-
this.blacklistedTime = blacklistedTime;
342-
this.recoverTime = recoverTime;
343-
}
344-
}
345-
346106
@Data
347107
private static class ConvertRequest {
348108
private String version; // CHECKSTYLE IGNORE THIS LINE

0 commit comments

Comments
 (0)