cube.js driver&&query&& cube schema 关系&&简单说明
从术语上应该是没有直接关系的,但是实际运行的时候彼此是有依赖的
driver 主要进行 sql 的查询处理,同时进行连接管理的处理,query 进行
sql 生成的处理(创建实际需要的sql 查询),cube schema 主要是定义
cube 的规则,query 实际上也是对于schema 抽象语言的sql 处理提供工具
如果阅读了api gateway 的处理机制,会发现cube.js 对于load 的处理流程如下
api gateway load 处理机制
- 解析请求(RequestContext)
- 基于compilerer(类似基于单例模式) 获取cube schema 的元数据定义生成查询sql (需要自定义驱动query的支持)
- 基于OrchestratorApi 进行sql 的查询执行(需要driver的支持)
代码说明
- load
public async load({ query, context, res,
const requestStarted = new Date();
try {
query = this.parseQueryParam(query);
this.log({
type: 'Load Request',
query
}, context);
const [queryType, normalizedQueries] = await this.getNormalizedQueries(query, context);
// 此处主要利用了compiler api 提供的能力进行cube schema 的编译处理,同时生成sql 查询(包含占位符的,后边的具体执行
需要依赖查询的条件)
const [metaConfigResult,
[
this.getCompilerApi(context).metaConfig({ requestId: context.requestId })
].concat(normalizedQueries.map(
async (normalizedQuery, index) => {
const loadRequestSQLStarted = new Date();
const sqlQuery = await this.getCompilerApi(context).getSql(
this.coerceForSqlQuery(normalizedQuery, context)
);
this.log({
type: 'Load Request SQL',
duration: this.duration(loadRequestSQLStarted),
query: normalizedQueries[index],
sqlQuery
}, context);
return sqlQuery;
}
))
);
let slowQuery = false;
const results = await Promise.all(normalizedQueries.map(async (normalizedQuery, index) => {
const sqlQuery = sqlQueries[index];
const annotation = prepareAnnotation(metaConfigResult, normalizedQuery);
const aliasToMemberNameMap = sqlQuery.aliasNameToMember;
const toExecute = {
query: sqlQuery.sql[0],
values: sqlQuery.sql[1],
continueWait: true,
renewQuery: normalizedQuery.renewQuery,
requestId: context.requestId
};
// 此处的adapterApi 实际上就是OrchestratorApi 核心是进行sql 的查询处理
const response = await this.getAdapterApi(context).executeQuery(toExecute);
const flattenAnnotation = {
};
slowQuery = slowQuery || Boolean(response.slowQuery);
return {
query: normalizedQuery,
data: transformData(
aliasToMemberNameMap,
flattenAnnotation,
response.data,
normalizedQuery,
queryType
),
lastRefreshTime: response.lastRefreshTime && response.lastRefreshTime.toISOString(),
refreshKeyValues: response.refreshKeyValues,
usedPreAggregations: response.usedPreAggregations
}),
annotation,
slowQuery: Boolean(response.slowQuery)
};
}));
this.log({
type: 'Load Request Success',
query,
duration: this.duration(requestStarted)
}, context);
if (queryType !== QUERY_TYPE.REGULAR_QUERY && props.queryType == null) {
throw new UserError(`'${queryType}' query type is not supported by the client. Please update the client.`);
}
if (props.queryType === 'multi') {
res({
queryType,
results,
pivotQuery: getPivotQuery(queryType, normalizedQueries),
slowQuery
});
} else {
res(results[0]);
}
} catch (e) {
this.handleError({
e, context, query, res, requestStarted
});
}
}
- getSQL 的方法定义
具体操作packages/cubejs-server-core/src/core/CompilerApi.js
使用了包装的compiler
async getSql(query, options) {
options = options || {};
const { includeDebugInfo } = options;
const dbType = this.getDbType();
const compilers = await this.getCompilers({ requestId: query.requestId });
let sqlGenerator = this.createQueryByDataSource(compilers, query);
if (!sqlGenerator) {
throw new Error(`Unknown dbType: ${dbType}`);
}
const dataSource = compilers.compiler.withQuery(sqlGenerator, () => sqlGenerator.dataSource);
if (dataSource !== 'default' && dbType !== this.getDbType(dataSource)) {
// TODO consider more efficient way than instantiating query
sqlGenerator = this.createQueryByDataSource(
compilers,
query,
dataSource
);
if (!sqlGenerator) {
throw new Error(`Can't find dialect for '${dataSource}' data source: ${this.getDbType(dataSource)}`);
}
}
// packages/cubejs-schema-compiler/src/compiler/DataSchemaCompiler.js 方法定义
return compilers.compiler.withQuery(sqlGenerator, () => ({
external: sqlGenerator.externalPreAggregationQuery(),
sql: sqlGenerator.buildSqlAndParams(),
timeDimensionAlias: sqlGenerator.timeDimensions[0] && sqlGenerator.timeDimensions[0].unescapedAliasName(),
timeDimensionField: sqlGenerator.timeDimensions[0] && sqlGenerator.timeDimensions[0].dimension,
order: sqlGenerator.order,
cacheKeyQueries: sqlGenerator.cacheKeyQueries(),
preAggregations: sqlGenerator.preAggregations.preAggregationsDescription(),
dataSource: sqlGenerator.dataSource,
aliasNameToMember: sqlGenerator.aliasNameToMember,
rollupMatchResults: includeDebugInfo ?
sqlGenerator.preAggregations.rollupMatchResultDescriptions() : undefined,
canUseTransformedQuery: sqlGenerator.preAggregations.canUseTransformedQuery()
}));
}
- adapterApi 的初始化
主要是api gateway 依赖的,基于缓存进行存储,由server-core 进行创建
protected apiGateway() {
if (!this.apiGatewayInstance) {
this.apiGatewayInstance = new ApiGateway(
this.options.apiSecret,
this.getCompilerApi.bind(this),
this.getOrchestratorApi.bind(this),
this.logger, {
standalone: this.standalone,
dataSourceStorage: this.orchestratorStorage,
basePath: this.options.basePath,
checkAuthMiddleware: this.options.checkAuthMiddleware,
checkAuth: this.options.checkAuth,
queryTransformer: this.options.queryTransformer,
extendContext: this.options.extendContext,
refreshScheduler: () => new RefreshScheduler(this),
}
);
}
return this.apiGatewayInstance;
}
getOrchestratorApi 的处理 (主要是基于配置定义driver 实例,同时cache方便复用)
public getOrchestratorApi(context: DriverContext): OrchestratorApi {
const orchestratorId = this.contextToOrchestratorId(context);
if (this.orchestratorStorage.has(orchestratorId)) {
return this.orchestratorStorage.get(orchestratorId);
}
const driverPromise = {};
let externalPreAggregationsDriverPromise;
const orchestratorApi = this.createOrchestratorApi({
getDriver: async (dataSource) => {
if (!driverPromise[dataSource || 'default']) {
orchestratorApi.addDataSeenSource(dataSource);
const driver = await this.options.driverFactory({
if (driver.setLogger) {
driver.setLogger(this.logger);
}
driverPromise[dataSource || 'default'] = driver.testConnection().then(() => driver).catch(e => {
driverPromise[dataSource || 'default'] = null;
throw e;
});
}
return driverPromise[dataSource || 'default'];
},
getExternalDriverFactory: this.options.externalDriverFactory && (async () => {
if (!externalPreAggregationsDriverPromise) {
const driver = await this.options.externalDriverFactory(context);
if (driver.setLogger) {
driver.setLogger(this.logger);
}
externalPreAggregationsDriverPromise = driver.testConnection().then(() => driver).catch(e => {
externalPreAggregationsDriverPromise = null;
throw e;
});
}
return externalPreAggregationsDriverPromise;
}),
redisPrefix: orchestratorId,
orchestratorOptions: this.orchestratorOptions(context)
});
this.orchestratorStorage.set(orchestratorId, orchestratorApi);
return orchestratorApi;
}
说明
cube.js 流程机制并不是很难,通过跟踪代码我们可以有整体的了解,后边会介绍下关于编译部分以及
OrchestratorApi部分的说明,编译部分cube.js 的设计还是很巧妙的利用了babel 以及ast 处理,同时为了
进行数据的隔离, 使用了node 的vm 实例进行编译处理
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
2020-02-04 pgspider docker 镜像
2020-02-04 pgspider基于pg 的高性能数据可视化sql 集群引擎
2019-02-04 What is Zeebe?
2019-02-04 pipenv 方便的python 开发工作流工具
2019-02-04 zeebe docker-compose 运行(包含monitor)
2019-02-04 几个微服务编排工具
2016-02-04 Mozilla Brick:一个Web组件Polyfill库