Skip to content

Commit b245569

Browse files
#AI commit# feat: AISQL支持starrocks切换 (#5363)
Co-authored-by: v-kkhuang <420895376@qq.com>
1 parent 3481d18 commit b245569

6 files changed

Lines changed: 266 additions & 20 deletions

File tree

‎linkis-computation-governance/linkis-entrance/src/main/java/org/apache/linkis/entrance/persistence/QueryPersistenceEngine.java‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
import org.apache.linkis.common.conf.Configuration$;
2121
import org.apache.linkis.common.exception.ErrorException;
22-
import org.apache.linkis.common.utils.JsonUtils;
2322
import org.apache.linkis.entrance.conf.EntranceConfiguration;
2423
import org.apache.linkis.entrance.exception.EntranceIllegalParamException;
2524
import org.apache.linkis.entrance.exception.EntranceRPCException;
@@ -47,7 +46,6 @@
4746
import java.util.List;
4847
import java.util.Map;
4948

50-
import com.fasterxml.jackson.core.JsonProcessingException;
5149
import org.slf4j.Logger;
5250
import org.slf4j.LoggerFactory;
5351

@@ -146,11 +144,13 @@ public void persist(JobRequest jobReq) throws ErrorException {
146144
JOBREQUEST_NOT_NULL.getErrorCode(), JOBREQUEST_NOT_NULL.getErrorDesc());
147145
}
148146
if (logger.isDebugEnabled()) {
149-
try {
150-
logger.debug("jobReq:" + JsonUtils.jackson().writeValueAsString(jobReq));
151-
} catch (JsonProcessingException e) {
152-
logger.debug("convert jobReq to string with error:" + e.getMessage());
153-
}
147+
logger.debug(
148+
"Persisting job request - id: {}, reqId: {}, submitUser: {}, executeUser: {}, status: {}",
149+
jobReq.getId(),
150+
jobReq.getReqId(),
151+
jobReq.getSubmitUser(),
152+
jobReq.getExecuteUser(),
153+
jobReq.getStatus());
154154
}
155155

156156
JobReqInsert jobReqInsert = new JobReqInsert(jobReq);

‎linkis-computation-governance/linkis-entrance/src/main/scala/org/apache/linkis/entrance/conf/EntranceConfiguration.scala‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ object EntranceConfiguration {
320320
CommonVars[String]("linkis.ai.sql.hive.template.keys", "hive,mapreduce").getValue
321321

322322
val AI_SQL_CREATORS: String =
323-
CommonVars[String]("linkis.ai.sql.support.creators", "IDE,MCP").getValue
323+
CommonVars[String]("linkis.ai.sql.support.creators", "IDE,MCP,nodeexecution").getValue
324324

325325
val AI_SQL_KEY: CommonVars[String] =
326326
CommonVars[String]("linkis.ai.sql.enable", "true")
@@ -369,6 +369,24 @@ object EntranceConfiguration {
369369

370370
val DOCTOR_HTTP_MAX_CONNECT = CommonVars("linkis.aisql.doctor.http.max.connect", 20).getValue
371371

372+
// StarRocks engine switch configurations
373+
val AISQL_STARROCKS_SWITCH = CommonVars("linkis.aisql.starrocks.switch", false)
374+
375+
val AISQL_DEFAULT_STARROCKS_ENGINE_TYPE =
376+
CommonVars("linkis.aisql.default.starrocks.engine.type", "jdbc-4").getValue
377+
378+
val AISQL_STARROCKS_TEMPLATE_KEYS =
379+
CommonVars("linkis.aisql.starrocks.template.keys", "starrocks")
380+
381+
val AISQL_STARROCKS_DATASOURCE_PREFIX =
382+
CommonVars("linkis.aisql.starrocks.datasource.prefix", "starrocks_")
383+
384+
val AISQL_STARROCKS_WHITELIST_USERS =
385+
CommonVars("linkis.aisql.starrocks.whitelist.users", "")
386+
387+
val AISQL_STARROCKS_WHITELIST_DEPARTMENTS =
388+
CommonVars("linkis.aisql.starrocks.whitelist.departments", "")
389+
372390
val SPARK_EXECUTOR_CORES = CommonVars.apply("spark.executor.cores", "2");
373391

374392
var SPARK_EXECUTOR_MEMORY = CommonVars.apply("spark.executor.memory", "6G");

‎linkis-computation-governance/linkis-entrance/src/main/scala/org/apache/linkis/entrance/interceptor/impl/AISQLTransformInterceptor.scala‎

Lines changed: 156 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import org.apache.linkis.common.conf.Configuration
2121
import org.apache.linkis.common.log.LogUtils
2222
import org.apache.linkis.common.utils.{Logging, Utils}
2323
import org.apache.linkis.common.utils.CodeAndRunTypeUtils.LANGUAGE_TYPE_AI_SQL
24+
import org.apache.linkis.datasourcemanager.common.domain.DataSource
2425
import org.apache.linkis.entrance.conf.EntranceConfiguration
2526
import org.apache.linkis.entrance.conf.EntranceConfiguration._
2627
import org.apache.linkis.entrance.interceptor.EntranceInterceptor
@@ -52,6 +53,7 @@ class AISQLTransformInterceptor extends EntranceInterceptor with Logging {
5253
val sqlLanguage: String = LANGUAGE_TYPE_AI_SQL
5354
val sparkEngineType: String = AI_SQL_DEFAULT_SPARK_ENGINE_TYPE
5455
val hiveEngineType: String = AI_SQL_DEFAULT_HIVE_ENGINE_TYPE
56+
val starrocksEngineType: String = AISQL_DEFAULT_STARROCKS_ENGINE_TYPE
5557
val labels: util.List[Label[_]] = jobRequest.getLabels
5658
val codeType: String = LabelUtil.getCodeType(labels)
5759
// engineType and creator have been verified in LabelCheckInterceptor.
@@ -105,24 +107,110 @@ class AISQLTransformInterceptor extends EntranceInterceptor with Logging {
105107
)
106108
} else {
107109
logger.info(s"start intelligent selection execution engine for ${jobRequest.getId}")
108-
val engineType: String =
109-
EntranceUtils.getDynamicEngineType(jobRequest.getExecutionCode, logAppender)
110+
111+
/**
112+
* Check for StarRocks engine switch if feature is enabled Priority: runtime parameters >
113+
* script comment > template configuration
114+
*/
115+
var forceEngineType: String = null
116+
if (AISQL_STARROCKS_SWITCH.getValue) {
117+
// 1. Check runtime parameters
118+
val runtimeEngineType = getRuntimeEngineType(jobRequest.getParams)
119+
120+
// 2. Check script comment
121+
val scriptEngineType = if (runtimeEngineType == null) {
122+
TemplateConfUtils.getCustomEngineType(
123+
jobRequest.getExecutionCode,
124+
org.apache.linkis.common.utils.CodeAndRunTypeUtils.getLanguageTypeByCodeType(codeType)
125+
)
126+
} else null
127+
128+
// Determine final engine type
129+
val targetEngineType = Option(runtimeEngineType)
130+
.orElse(Option(scriptEngineType))
131+
.orNull
132+
133+
// If StarRocks engine is specified
134+
if ("starrocks".equalsIgnoreCase(targetEngineType)) {
135+
// Check whitelist
136+
val (user, creator) = LabelUtil.getUserCreator(jobRequest.getLabels)
137+
if (!isUserInStarRocksWhitelist(user)) {
138+
logger.warn(
139+
s"User $user is not in StarRocks whitelist for task ${jobRequest.getId}, using default engine selection"
140+
)
141+
logAppender.append(
142+
LogUtils.generateWarn(
143+
s"User $user is not in StarRocks whitelist, using default engine selection\n"
144+
)
145+
)
146+
} else {
147+
forceEngineType = "starrocks"
148+
}
149+
}
150+
}
151+
val engineType: String = {
152+
EntranceUtils.getDynamicEngineType(
153+
jobRequest.getExecutionCode,
154+
logAppender,
155+
forceEngineType
156+
)
157+
}
110158
if ("hive".equals(engineType)) {
111159
changeEngineLabel(hiveEngineType, labels)
112-
logAppender.append(
113-
LogUtils.generateInfo(s"use $hiveEngineType by intelligent selection.\n")
114-
)
115160
currentEngineType = hiveEngineType
161+
} else if ("starrocks".equals(engineType)) {
162+
163+
Utils.tryCatch {
164+
val dataSource: DataSource = EntranceUtils.getDatasourceByDatasourceTypeAndUser(
165+
"starrocks",
166+
jobRequest.getSubmitUser,
167+
jobRequest.getExecuteUser
168+
)
169+
if (dataSource != null) {
170+
val dsParams: util.Map[String, AnyRef] = new util.HashMap[String, AnyRef]()
171+
dsParams.put("wds.linkis.engine.runtime.datasource", dataSource.getDataSourceName)
172+
TaskUtils.addRuntimeMap(jobRequest.getParams, dsParams)
173+
changeEngineLabel(starrocksEngineType, labels)
174+
currentEngineType = starrocksEngineType
175+
logAppender.append(
176+
LogUtils.generateInfo(
177+
s"use starrocks engine with datasource name : ${dataSource.getDataSourceName} \n"
178+
)
179+
)
180+
} else {
181+
// use hive for datasource not exists
182+
changeEngineLabel(hiveEngineType, labels)
183+
currentEngineType = hiveEngineType
184+
logger.warn(
185+
s"Failed to select starrocks engine, for user ${jobRequest.getExecuteUser} datasource not exists."
186+
)
187+
logAppender.append(
188+
LogUtils.generateInfo(
189+
s"Failed to select starrocks engine, ${jobRequest.getExecuteUser} datasource does not exist. now use $currentEngineType \n"
190+
)
191+
)
192+
}
193+
} { t =>
194+
// use hive for exception
195+
changeEngineLabel(hiveEngineType, labels)
196+
currentEngineType = hiveEngineType
197+
logger.warn("Failed to select starrocks engine: ", t)
198+
logAppender.append(
199+
LogUtils.generateInfo(
200+
s"Failed to select starrocks engine, service exception. now use $currentEngineType \n"
201+
)
202+
)
203+
}
116204
} else {
117205
changeEngineLabel(sparkEngineType, labels)
118-
logAppender.append(
119-
LogUtils.generateInfo(s"use $sparkEngineType by intelligent selection.\n")
120-
)
121206
currentEngineType = sparkEngineType
122207
}
123208
logger.info(
124209
s"end intelligent selection execution engine, and engineType is ${currentEngineType} for ${jobRequest.getId}."
125210
)
211+
logAppender.append(
212+
LogUtils.generateInfo(s"use $currentEngineType by intelligent selection.\n")
213+
)
126214
EntranceUtils.dealsparkDynamicConf(jobRequest, logAppender, jobRequest.getParams)
127215
}
128216

@@ -179,4 +267,64 @@ class AISQLTransformInterceptor extends EntranceInterceptor with Logging {
179267
labels.add(newEngineTypeLabel)
180268
}
181269

270+
/**
271+
* Get engine type from runtime parameters
272+
* @param params
273+
* job request parameters
274+
* @return
275+
* engine type, such as "starrocks", null if not found
276+
*/
277+
private def getRuntimeEngineType(params: util.Map[String, AnyRef]): String = {
278+
if (params == null) return null
279+
280+
val runtimeParams = TaskUtils.getRuntimeMap(params)
281+
if (runtimeParams == null) return null
282+
283+
val engineType = runtimeParams.get(TemplateConfUtils.confEngineTypeKey)
284+
if (engineType != null) engineType.toString else null
285+
}
286+
287+
/**
288+
* Check if user is in StarRocks whitelist
289+
* @param submitUser
290+
* the user who submits the task
291+
* @return
292+
* true if user is in whitelist or whitelist is empty (allow all users), false otherwise
293+
*/
294+
private def isUserInStarRocksWhitelist(submitUser: String): Boolean = {
295+
val whitelistUsers = AISQL_STARROCKS_WHITELIST_USERS.getValue
296+
val whitelistDepartments = AISQL_STARROCKS_WHITELIST_DEPARTMENTS.getValue
297+
298+
// If both whitelists are empty, allow all users
299+
if (StringUtils.isBlank(whitelistUsers) && StringUtils.isBlank(whitelistDepartments)) {
300+
return true
301+
}
302+
303+
// Check user whitelist
304+
if (StringUtils.isNotBlank(whitelistUsers)) {
305+
val users = whitelistUsers.split(",").map(_.trim)
306+
if (users.contains(submitUser)) {
307+
logger.info(s"User $submitUser is in StarRocks whitelist (user)")
308+
return true
309+
}
310+
}
311+
312+
// Check department whitelist
313+
if (StringUtils.isNotBlank(whitelistDepartments)) {
314+
val userDepartmentId = EntranceUtils.getUserDepartmentId(submitUser)
315+
if (StringUtils.isNotBlank(userDepartmentId)) {
316+
val departments = whitelistDepartments.split(",").map(_.trim)
317+
if (departments.contains(userDepartmentId)) {
318+
logger.info(
319+
s"User $submitUser (department: $userDepartmentId) is in StarRocks whitelist (department)"
320+
)
321+
return true
322+
}
323+
}
324+
}
325+
326+
logger.warn(s"User $submitUser is not in StarRocks whitelist")
327+
false
328+
}
329+
182330
}

‎linkis-computation-governance/linkis-entrance/src/main/scala/org/apache/linkis/entrance/interceptor/impl/Explain.scala‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ object SQLExplain extends Explain {
219219
EntranceErrorCode.EXECUTION_CODE_ISNULL.getDesc
220220
)
221221
}
222-
logger.debug(s"after sql limit code is ${requestPersistTask.getExecutionCode}")
222+
logger.debug(s"SQL limit applied.")
223223
}
224224

225225
private def findRealSemicolonIndex(tempCode: String): Array[Int] = {

‎linkis-computation-governance/linkis-entrance/src/main/scala/org/apache/linkis/entrance/interceptor/impl/TemplateConfUtils.scala‎

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ object TemplateConfUtils extends Logging {
5555

5656
val confTemplateNameKey = "ec.resource.name"
5757
val confFixedEngineConnLabelKey = "ec.fixed.sessionId"
58+
val confEngineTypeKey = "ec.engine.type"
5859

5960
/**
6061
* 按模板uuid缓存模板配置
@@ -233,6 +234,40 @@ object TemplateConfUtils extends Logging {
233234
templateConfName
234235
}
235236

237+
/**
238+
* Get user-defined engine type from script code
239+
*
240+
* @param code
241+
* :code
242+
* @param languageType
243+
* :sql,hql,python,scala
244+
* @return
245+
* String engine type, such as "starrocks", "spark", "hive", null if not found
246+
*/
247+
def getCustomEngineType(code: String, languageType: String): String = {
248+
if (StringUtils.isBlank(code) || StringUtils.isBlank(languageType)) {
249+
return null
250+
}
251+
252+
val confPattern = languageType.toLowerCase match {
253+
case x if x.contains("python") || x.contains("shell") =>
254+
s"##@set\\s+${confEngineTypeKey}\\s*=\\s*([^\\s#]+)".r
255+
case x if x.contains("scala") =>
256+
s"///@set\\s+${confEngineTypeKey}\\s*=\\s*([^\\s/]+)".r
257+
case _ =>
258+
s"---@set\\s+${confEngineTypeKey}\\s*=\\s*([^\\s-]+)".r
259+
}
260+
261+
val codeRes = code.replaceAll("\r\n", "\n")
262+
confPattern.findFirstMatchIn(codeRes) match {
263+
case Some(m) =>
264+
val engineType = m.group(1).trim
265+
logger.info(s"Get custom engine type: $engineType from script")
266+
engineType
267+
case None => null
268+
}
269+
}
270+
236271
def dealWithTemplateConf(jobRequest: JobRequest, logAppender: lang.StringBuilder): JobRequest = {
237272
jobRequest match {
238273
case requestPersistTask: JobRequest =>
@@ -245,7 +280,7 @@ object TemplateConfUtils extends Logging {
245280
// only for Creator:IDE, try to get template conf name from code string. eg:---@set ec.resource.name=xxxx
246281
val codeType = LabelUtil.getCodeType(jobRequest.getLabels)
247282
val (user, creator) = LabelUtil.getUserCreator(jobRequest.getLabels)
248-
if (EntranceConfiguration.DEFAULT_REQUEST_APPLICATION_NAME.getValue.equals(creator)) {
283+
if (EntranceConfiguration.DEFAULT_REQUEST_APPLICATION_NAME.getValue.contains(creator)) {
249284
templateName = getCustomTemplateConfName(jobRequest, codeType, logAppender)
250285
if (StringUtils.isNotBlank(templateName)) {
251286
logAppender.append(

0 commit comments

Comments
 (0)