- RFC PR: datafuselabs/databend#6503
- Tracking Issue: datafuselabs/databend#6215
概述
添加新的 PRESIGN
SQL 语句,以便用户可以生成用于上传或下载的预签名 URL。
动机
Databend 支持通过内部 Stage 加载数据:
- 调用 HTTP API
upload_to_stage
上传文件:curl -H "x-databend-stage-name:my_int_stage" -F "upload=@./books.csv" -XPUT http://localhost:8000/v1/upload_to_stage
- 调用
COPY INTO
复制数据:COPY INTO books FROM '@my_int_stage'
此工作流程的吞吐量受限于 databend 的 HTTP API:upload_to_stage
。我们可以通过允许用户直接上传到我们的后端存储来提高吞吐量。例如,我们可以使用 AWS 认证请求:使用查询参数 生成预签名 URL。这样,用户可以直接将内容上传到 AWS s3,而无需通过 databend。
此外,PRESIGN
可以减少网络开销。所有流量都直接从用户端到 s3,无需额外的 databend 实例成本。
指南级解释
用户可以生成用于读取的 URL:
MySQL [(none)]> PRESIGN @my_stage/books.csv
+--------+---------+---------------------------------------------------------------------------------+
| method | headers | url |
+--------+---------+---------------------------------------------------------------------------------+
| GET | [] | https://example.s3.amazonaws.com/books.csv?X-Amz-Algorithm=AWS4-HMAC-SHA256&... |
+--------+---------+---------------------------------------------------------------------------------+
默认情况下,预签名 URL 将在 1 小时后过期。用户可以指定过期时间为 2 小时,如下所示:
MySQL [(none)]> PRESIGN @my_stage/books.csv EXPIRE=7200
+--------+---------+---------------------------------------------------------------------------------+
| method | headers | url |
+--------+---------+---------------------------------------------------------------------------------+
| GET | {} | https://example.s3.amazonaws.com/books.csv?X-Amz-Algorithm=AWS4-HMAC-SHA256&... |
+--------+---------+---------------------------------------------------------------------------------+
默认情况下,预签名 URL 是为 download
操作生成的。用户可以创建用于 upload
的 URL,如下所示:
MySQL [(none)]> PRESIGN UPLOAD @my_stage/books.csv EXPIRE=7200
+--------+---------+---------------------------------------------------------------------------------+
| method | headers | url |
+--------+---------+---------------------------------------------------------------------------------+
| PUT | {} | https://example.s3.amazonaws.com/books.csv?X-Amz-Algorithm=AWS4-HMAC-SHA256&... |
+--------+---------+---------------------------------------------------------------------------------+
如果 presign
返回的 headers
不为空,用户应在实际请求中包含它们。
MySQL [(none)]> PRESIGN UPLOAD @my_stage/books.csv
+--------+--------------------------+---------------------------------------------------------------------------------+
| method | headers | url |
+--------+--------------------------+---------------------------------------------------------------------------------+
| PUT | {'x-amz-key': 'value'} | https://example.s3.amazonaws.com/books.csv?X-Amz-Algorithm=AWS4-HMAC-SHA256&... |
+--------+--------------------------+---------------------------------------------------------------------------------+
参考级解释
PRESIGN
将作为语句而不是函数实现,以便我们可以同时返回 HTTP 方法、headers 和 URL。
大部分工作已经通过 Apache OpenDAL presign 完成。
语法将是:
PRESIGN [(DOWNLOAD | UPLOAD)] <location> [EXPIRE = <SECONDS>]
在 databend 中,我们将:
- 在解析器中添加
PRESIGN
(仅在新规划器中) - 实现
presign
解释器 - 为 presign 添加状态测试
- 添加关于
presign
的文档
缺点
无。
基本原理和替代方案
Snowflake GET_PRESIGNED_URL
Snowflake 有一个名为 GET_PRESIGNED_URL
的 SQL 函数。
GET_PRESIGNED_URL( @<stage_name> , '<relative_file_path>' , [ <expiration_time> ] )
用户只能获取用于下载的 Stage 中文件的预签名 URL:
select get_presigned_url(@images_stage, 'us/yosemite/half_dome.jpg', 3600);
+================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================-------+
| GET_PRESIGNED_URL(@IMAGES_STAGE, 'US/YOSEMITE/HALF_DOME.JPG', 3600) |
|================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================-------|
| http://myaccount.s3.amazonaws.com/national_parks/us/yosemite/half_dome.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxAus-west-xxxxxxxxxaws1_request&X-Amz-Date=20200625T162738Z&X-Amz-Expires=3600&X-Amz-Security-Token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-Amz-SignedHeaders=host&X-Amz-Signature=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
+================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================-------+
Snowflake 不允许通过 SQL 生成用于上传的预签名 URL。相反,他们在 SDK 中实现了类似的功能。
以 snowflake golang SDK 为例:
他们 构建传输客户端 使用短期 s3 令牌:
func (util *snowflakeS3Client) createClient(info *execResponseStageInfo, useAccelerateEndpoint bool) (cloudClient, error) {
stageCredentials := info.Creds
var resolver s3.EndpointResolver
if info.EndPoint != "" {
resolver = s3.EndpointResolverFromURL("https://" + info.EndPoint) // FIPS endpoint
}
return s3.New(s3.Options{
Region: info.Region,
Credentials: aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(
stageCredentials.AwsKeyID,
stageCredentials.AwsSecretKey,
stageCredentials.AwsToken)),
EndpointResolver: resolver,
UseAccelerate: useAccelerateEndpoint,
}), nil
}
execResponseStageInfo
通过 内部 API 获取:
jsonBody, err := json.Marshal(req)
if err != nil {
return nil, err
}
data, err := sc.rest.FuncPostQuery(ctx, sc.rest, &url.Values{}, headers,
jsonBody, sc.rest.RequestTimeout, requestID, sc.cfg)
if err != nil {
return data, err
}
code := -1
if data.Code != "" {
code, err = strconv.Atoi(data.Code)
if err != nil {
return data, err
}
}
Databend 倾向于在内核中实现相关功能,而不是在 SDK 中。
未解决的问题
无。
未来的可能性
扩展对位置的支持
我们可以扩展对 COPY
类似位置的支持:
PRESIGN 's3://bucket/books.csv'
多部分支持
我们可以生成多部分相关操作,以允许上传单个 10TB 文件:
PRESIGN UPLOAD_PART 's3://bucket/books.csv.zst'