원격 서비스 입출력 데이터 형식
원격 서비스 입출력 데이터 형식 (Remote service input and output data formats)
이 문서는 Snowflake가 원격 서비스(remote service)로 데이터를 보내거나 받을 때 지켜야 하는 데이터 형식을 설명해요. 외부 함수를 실행할 때 Snowflake가 보내고 기대하는 데이터 형식을 정확히 이해하면, 원격 서비스가 올바르게 요청을 처리하고 응답할 수 있어요.
본문
Snowflake가 원격 서비스로 데이터를 보내거나 원격 서비스로부터 데이터를 받을 때, 데이터는 올바르게 형식화되어야 해요. 이 문서는 올바른 데이터 형식에 대한 정보를 제공합니다. Snowflake로부터 받고 Snowflake에게 반환하는 데이터는 적절한 데이터 타입이어야 합니다.
예를 들어 외부 함수를 실행할 때 Snowflake는 이 문서에서 설명하는 형식으로 데이터를 보내고 기대합니다. Snowflake는 원격 서비스에 직접 데이터를 보내지 않고 프록시 서비스(proxy service)에 보냅니다(자세한 내용은 외부 함수 소개 문서 참고). 따라서 프록시 서비스는 Snowflake와 호환되는 형식으로 데이터를 받고(그리고 반환하고) 있어야 해요. 프록시 서비스는 보통 데이터를 변경 없이 통과시키지만, 원격 서비스와 Snowflake 양쪽의 요구 사항을 모두 충족하도록 데이터를 재형식화(보내고 받는 양쪽 모두)할 수도 있어요.
간단하게, 그리고 Snowflake가 보내고 받기를 기대하는 형식을 설명하기 위해 이 절의 대부분의 예시는 원격 서비스가 Snowflake가 기대하는 것과 같은 형식으로 데이터를 읽고 쓰며, 프록시 서비스가 양방향 모두 데이터를 변경 없이 통과시킨다고 가정합니다.
Snowflake가 보내는 데이터 형식 (Data format sent by Snowflake)
Snowflake의 각 HTTP 요청은 POST 또는 GET이에요.
- POST 요청은 헤더(headers)와 요청 본문(request body)을 포함해요. 요청 본문은 행(batch of rows)의 배치를 포함합니다.
- GET은 헤더만 포함하며, 원격 서비스가 비동기적으로 결과를 반환할 때 폴링(polling)에만 사용돼요.
본문 형식 (Body format)
POST 요청의 본문에는 JSON 형식으로 직렬화된 데이터가 들어 있어요. JSON 스키마는 다음과 같아요:
- 최상위는 JSON 객체(이름/값 쌍의 집합, "dictionary"라고도 함)예요.
- 현재 그 객체에는 정확히 하나의 항목이 있으며, 그 항목의 키는 "data"로 이름이 지정돼 있어요.
- "data" 항목의 값은 JSON 배열이며, 배열에서:
- 각 요소는 데이터의 한 행(row)이에요.
- 각 데이터 행은 하나 이상의 컬럼으로 이루어진 JSON 배열이에요.
- 첫 번째 컬럼은 항상 행 번호(배치 내 0부터 시작하는 행의 인덱스)예요.
- 나머지 컬럼들은 함수에 전달되는 인자(arguments)를 포함해요.
- 데이터 타입은 다음과 같이 직렬화됩니다:
- 숫자(Numbers)는 JSON 숫자로 직렬화돼요.
- 부울(Booleans)은 JSON 부울로 직렬화돼요.
- 문자열(Strings)은 JSON 문자열로 직렬화돼요.
- 객체(Objects)는 JSON 객체로 직렬화돼요.
- 그 외 지원되는 모든 데이터 타입은 JSON 문자열로 직렬화돼요.
- 날짜, 시간, 타임스탬프는 문자열로 직렬화돼요. 이 데이터 타입들을 문자열로 형식화하는 자세한 내용은 날짜와 시간 입출력 형식(Date and time input and output formats), 변환 함수의 날짜와 시간 형식(Date and time formats in conversion functions) 문서를 참고하세요.
- 이진(Binary) 컬럼은 문자열로 직렬화돼요. 자세한 내용은 지원되는 이진 형식 개요(Overview of supported binary formats) 문서를 참고하세요.
- NULL은 JSON null로 직렬화돼요.
각 플랫폼에서 원격 서비스로 데이터를 추출하는 예시는 다음을 참고하세요:
- AWS: 원격 서비스 만들기(Create the Remote Service, AWS의 Lambda Function)
- Azure: 원격 서비스 만들기(Create the Remote Service, Azure Function)
선택적으로, JSON은 네트워크 전송을 위해 압축할 수 있어요. 압축은 CREATE EXTERNAL FUNCTION에 문서화돼 있어요.
본문 예시 (Body example)
다음은 시그니처가 f(integer, varchar, timestamp)인 외부 함수에 대한 직렬화된 요청 예시예요. 첫 번째 컬럼은 배치 내 행 번호이고, 다음 세 값은 외부 함수의 인자입니다.
{
"data": [
[0, 10, "Alex", "2014-01-01 16:00:00"],
[1, 20, "Steve", "2015-01-01 16:00:00"],
[2, 30, "Alice", "2016-01-01 16:00:00"],
[3, 40, "Adrian", "2017-01-01 16:00:00"]
]
}
헤더 형식 (Header format)
헤더 정보는 일반적으로 키/값 쌍의 집합으로 원격 서비스에 제공돼요. 헤더 정보에는 다음이 포함됩니다:
-
다음 HTTP 헤더들:
- 요청 본문에서 데이터가 어떻게 직렬화되는지 설명하는 헤더:
- "sf-external-function-format": 현재 항상 "json"으로 설정돼요.
- "sf-external-function-format-version": 현재 항상 "1.0"으로 설정돼요.
- "sf-external-function-current-query-id": 이 외부 함수를 호출한 쿼리의 쿼리 ID(query ID)를 포함해요. 이 값을 사용해서 Snowflake 쿼리를 원격 서비스 호출과 연관 지을 수 있어요(예: 디버깅에 도움).
- "sf-external-function-query-batch-id": 배치 ID(batch ID)는 이 요청으로 처리되는 행들의 특정 배치를 고유하게 식별해요. 원격 서비스는 이 ID를 사용해 처리 중인 배치의 상태를 추적할 수 있어요. 이 ID는 오류로 인해 요청이 재시도될 때 멱등성 토큰(idempotency token)으로도 사용할 수 있어요. 이 ID는 원격 서비스의 요청 로깅/추적에도 사용할 수 있어요. GET의 배치 ID는 해당 POST의 배치 ID와 같아요. 배치 ID는 Snowflake가 생성하는 불투명한 값이며, 형식이 향후 릴리스에서 바뀔 수 있으므로 원격 서비스는 특정 형식에 의존하거나 값을 해석하려고 해서는 안 돼요.
- SQL 쿼리에서 호출된 외부 함수의 시그니처(이름과 인자 타입)와 반환 타입을 설명하는 헤더. 이 값들은 Snowflake 식별자에 표준이 아닌 문자가 포함될 수 있어서, 정보의 base64 버전이 포함되고 비-base64 버전에서는 비표준 문자가 공백으로 대체돼요. 해당 헤더는 다음과 같아요:
sf-external-function-namesf-external-function-name-base64sf-external-function-signaturesf-external-function-signature-base64sf-external-function-return-typesf-external-function-return-type-base64
예를 들어,
ext_func(n integer) returns varchar함수에 대해 보내지는 헤더는 다음과 같아요:sf-external-function-name: ext_funcsf-external-function-name-base64:sf-external-function-signature: (N NUMBER)sf-external-function-signature-base64:sf-external-function-return-type: VARCHAR(134217728)sf-external-function-return-type-base64:
SQL
INTEGER값이 SQLNUMBER로 취급되므로, 타입INTEGER로 선언된 SQL 인자는 타입NUMBER로 설명돼요.CREATE EXTERNAL FUNCTION의 "headers" 및 "context_headers" 속성에 설명된 추가적인 선택적 메타데이터.
- 요청 본문에서 데이터가 어떻게 직렬화되는지 설명하는 헤더:
헤더 접근 예시 (Header access example)
헤더를 Python 사전으로 받는 AWS Lambda 함수 안에서 "sf-external-function-signature" 헤더를 추출하려면 다음을 실행하세요:
def handler(event, context):
request_headers = event["headers"]
signature = request_headers["sf-external-function-signature"]
다른 언어와 다른 클라우드 플랫폼에서는 세부 사항이 달라질 수 있어요. AWS에서 개발된 원격 서비스의 경우 헤더와 lambda 프록시 통합에 대한 더 많은 정보가 AWS API Gateway 문서에 있습니다.
Snowflake가 받는 데이터 형식 (Data format received by Snowflake)
본문 형식 (Body format)
원격 서비스가 배치 처리를 마치면 Snowflake가 보낸 데이터 형식과 유사한 JSON 형식으로 데이터를 Snowflake에 다시 보내야 해요. Snowflake에 반환되는 JSON 응답은 Snowflake가 보낸 각 행마다 하나의 행을 포함해야 해요. 각 반환 행은 두 개의 값을 포함합니다:
- 행 번호(배치 내 0부터 시작하는 행의 인덱스)
- 해당 행에 대해 함수가 반환한 값. 값은 복합 값(예: OBJECT)일 수 있지만, 모든 스칼라 Snowflake 함수(외부 함수든 아니든)는 단일 값을 반환하므로 정확히 하나의 값이어야 해요.
Snowflake가 응답을 요청과 연관 지을 수 있도록, 반환 데이터의 행 번호는 Snowflake가 보낸 데이터의 행 번호와 일치해야 하며 받은 순서와 같은 순서로 반환되어야 해요.
본문 접근 예시 (Body access example)
다음 JSON 예시는 각각 행 번호가 앞에 붙은, OBJECT 값을 포함하는 두 개의 행을 보여줘요:
{
"data":
[
[ 0, { "City" : "Warsaw", "latitude" : 52.23, "longitude" : 21.01 } ],
[ 1, { "City" : "Toronto", "latitude" : 43.65, "longitude" : -79.38 } ]
]
}
Python으로 이런 반환 행을 하나 구성하려면 다음 코드를 사용할 수 있어요:
...
row_number = 0
output_value = {}
output_value["city"] = "Warsaw"
output_value["latitude"] = 21.01
output_value["longitude"] = 52.23
row_to_return = [row_number, output_value]
...
SQL로 반환된 행의 OBJECT 값에 접근하려면 반정형 데이터 탐색(Traversing Semi-structured Data) 문서에 설명된 표기법을 사용하세요. 예를 들어:
select val:city, val:latitude, val:longitude
from (select ext_func_city_lat_long(city_name) as val from table_of_city_names);
헤더 형식 (Header format)
응답에는 다음 선택적 HTTP 헤더도 포함될 수 있어요:
Content-MD5: Snowflake는 선택적Content-MD5헤더를 사용해 응답의 무결성을 확인해요. 이 헤더가 응답에 포함되면 Snowflake는 응답 본문에 MD5 체크섬을 계산해서 반환된 헤더의 해당 체크섬과 일치하는지 확인해요. 값이 일치하지 않으면 SQL 쿼리가 실패해요. 체크섬은 헤더로 반환되기 전에 base64 표현으로 인코딩되어야 해요. 아래 예시 코드를 참고하세요.
선택적으로, JSON은 네트워크 전송을 위해 압축할 수 있어요. 압축은 CREATE EXTERNAL FUNCTION에 문서화돼 있어요. 타임아웃과 재시도에 대한 정보는 타임아웃 오류 고려하기(Account for timeout errors)와 원격 서비스가 각 행을 정확히 한 번만 받는다고 가정하지 마세요(Do not assume that the remote service is passed each row exactly once) 문서를 참고하세요.
상태 코드 (Status code)
응답에는 HTTP 상태 코드도 포함돼요. Snowflake가 인식하는 HTTP 상태 코드는 다음과 같아요:
| Code | Description |
|---|---|
| 200 | 배치가 성공적으로 처리됨 (Batch processed successfully) |
| 202 | 배치를 받았으며 아직 처리 중 (Batch received and still being processed) |
그 외의 값들은 오류로 취급돼요.
응답 생성 예시 (Response creation example)
아래의 예시 Python 코드는 적절한 응답(HTTP 응답 코드, 처리된 데이터, 그리고 선택적인 MD5 헤더 포함)을 반환해요. 이 예시는 AWS Lambda 함수를 기반으로 하며, 플랫폼에 따라 일부 코드를 수정해야 할 수 있어요.
import json
import hashlib
import base64
def handler(event, context):
# The return value should contain an array of arrays (one inner array
# per input row for a scalar function).
array_of_rows_to_return = [ ]
...
json_compatible_string_to_return = json.dumps({"data" : array_of_rows_to_return})
# Calculate MD5 checksum for the response
md5digest = hashlib.md5(json_compatible_string_to_return.encode('utf-8')).digest()
response_headers = {
'Content-MD5' : base64.b64encode(md5digest)
}
# Return the HTTP status code, the processed data, and the headers
# (including the Content-MD5 header).
return {
'statusCode': 200,
'body': json_compatible_string_to_return,
'headers': response_headers
}
더 알아보기 (Learn more)
- 외부 함수 소개 (external-functions-introduction)
- 외부 함수 구현 (external-functions-implementation)
- 외부 함수 보안 (external-functions-security)
- 원격 서비스 만들기 (external-functions-creating-gcp-ui-remote-service)