클린룸에서 사용자 지정 함수 업로드 및 실행¶
개요¶
사용자 지정 Python UDFs 및 UDTFs를 클린룸으로 업로드하고 템플릿에서 실행하여 복잡한 데이터 작업을 수행할 수 있습니다. 이러한 작업으로는 단일 단계 또는 다단계 흐름 중에 쿼리 내에서 머신 러닝 또는 사용자 지정 데이터 조작을 하는 것이 있습니다. Python은 사용자 지정 UDFs에 대해 지원되는 유일한 코딩 언어입니다.
업로드한 코드는 `승인된 Python 패키지 번들<https://repo.anaconda.com/pkgs/snowflake/>`_ 및 :ref:`Snowpark API<label-dcr_snowpark_udf>`에서 패키지를 가져오고 사용할 수 있습니다.
공급자와 컨슈머 모두 사용자 지정 Python 코드를 클린룸에 업로드할 수 있지만, 공급자와 컨슈머의 프로세스는 다릅니다. 업로드된 코드 번들은 각각 서로를 호출하는 여러 함수를 정의할 수 있지만, 하나의 번들은 하나의 처리기 함수만 노출합니다. 이 처리기 함수는 클린룸을 사용하는 모든 사용자가 만들거나 실행하는 템플릿으로 호출할 수 있습니다. 코드가 내부 테이블을 생성하는 경우 :doc:`/user-guide/cleanrooms/multistep-flows`에 설명된 대로 이러한 테이블에 액세스할 수 있습니다.
업로드된 코드는 삭제할 수 없지만 업데이트할 수 있습니다.
이 페이지에서는 공급자 또는 컨슈머 역할로 사용자 지정 Python UDFs 및 UDTFs를 업로드하고 실행하는 방법을 보여줍니다.
팁
클린룸에서 Python UDFs를 직접 개발하는 방법에 대한 배경 정보는 다음 항목을 참조하세요.
UDFs가 Snowflake에서 작동하는 방식: Snowflake에서 Python 함수를 작성하는 방법에 대한 일반적인 배경 정보가 나와 있습니다.
Snowflake에서 UDTFs를 작성하는 방법: 함수에서 테이블을 반환하려는 경우에 참조합니다.
:doc:`사용자 지정 템플릿을 만들고 클린룸에 업로드하는 방법: </user-guide/cleanrooms/demo-flows/custom-templates>`UDFs/UDTFs는 사용자 지정 템플릿에서 호출됩니다.
:doc:`클린룸에서 Snowpark 사용 </user-guide/cleanrooms/demo-flows/snowpark>`(Snowpark에서 UDFs를 호출하려는 경우)
사용자 지정 함수 업데이트¶
업로드한 기존 함수를 업로드하거나 덮어쓸 수 있지만, 기존 함수를 삭제할 수는 없습니다.
이전에 업로드한 서명과 정확히 동일한 서명을 가진 함수를 업로드하면 기존 함수를 덮어씁니다. 서명은 외부 처리기의 함수 이름과 모든 매개 변수의 데이터 타입을 동일한 순서대로 나열한 것으로, 대/소문자를 구분하지 않습니다. 매개 변수 이름은 중요하지 않습니다. 다른 계정에서 업로드한 함수는 덮어쓸 수 없습니다.
함수를 업데이트할 때 서명이 일치해야 하므로 기존 함수의 서명을 변경할 수 없습니다. 함수 ``foo(name VARIANT age INTEGER)``를 업로드한 다음, 함수 ``foo(name VARIANT age FLOAT)``를 업로드하면 인자 유형이 다르기 때문에 첫 번째 함수 외에 두 번째 함수가 클린룸에 추가됩니다.
공급자 제출 코드¶
공급자가 제출한 함수는 인라인 코드로 업로드하거나 Snowflake 스테이지에서 업로드할 수 있습니다. 두 기술 모두 여기에서 살펴봅니다.
업로드한 코드는 기본적으로 `승인된 Python 패키지 세트<https://repo.anaconda.com/pkgs/snowflake/>`_에서 패키지를 가져와 사용할 수 있습니다. 기본 패키지가 아닌 패키지가 필요한 경우 :ref:`클린룸의 Snowpark Container Services<label-dcr_snowpark_spcs>`를 사용하여 코드를 호스트해야 합니다.
업로드된 코드는 물론 자신의 코드도 볼 수 없으므로, 클린룸에 업로드한 코드의 복사본을 그대로 포함해야 합니다.
팁
공급자가 작성한 코드를 업데이트한 후 기본 릴리스 지시문을 업데이트한 다음, ``provider.create_or_update_cleanroom_listing``을 호출하여 변경 사항을 컨슈머에게 전파해야 합니다. ``provider.create_or_update_cleanroom_listing``을 호출하지 않으면 현재 클린룸을 사용 중인 컨슈머에게 기본 버전이 업데이트되지 않습니다.
다음에서는 공급자가 클린룸에 코드를 추가하는 방법을 개략적으로 살펴봅니다.
공급자는 일반적인 방식으로 클린룸을 만들고 구성합니다.
공급자는 ``provider.load_python_into_cleanroom``을 호출하여 코드를 업로드합니다. 해당 프로시저 내에서 직접 :ref:`코드를 인라인으로 업로드<label-dcr_upload_code_inline>`하거나 :ref:`스테이지에 코드 파일을 업로드<label-dcr_provider_code_from_stage>`한 다음, 해당 프로시저에 스테이지 위치를 제공할 수 있습니다.
코드에 여러 함수가 포함될 수 있지만, 각 업로드에 대해 하나의 처리기만 노출됩니다. 여러 함수를 템플릿에 노출하려면 ``provider.load_python_into_cleanroom``을 호출하여 각 처리기를 업로드합니다.
코드 업로드에 성공할 때마다 클린룸의 새 패치 버전이 생성됩니다. 그러면 새 패치 번호를 사용하여 ``provider.set_default_release_directive``를 호출함으로써 기본 버전을 증분해야 합니다. 클린룸이 외부에 노출되는 경우 코드를 설치하기 전에 보안 검사가 실행되며, 기본 버전을 증분하기 전에 ``provider.view_cleanroom_scan_status``를 호출하여 보안 검사를 통과했는지 확인해야 합니다.
코드를 호출하는 사용자 지정 템플릿을 만들고 업로드합니다. 템플릿은
cleanroom
범위를 사용하여 처리기 함수를 호출합니다(즉,cleanroom.my_function
). 예를 들어, 업로드한 사용자 지정simple_add
함수를 호출하는 템플릿은 다음과 같을 수 있습니다.SELECT cleanroom.simple_add(1, 2), cleanroom.simple_add({{ price | sqlsafe | int }}, {{ tax | sqlsafe | int }})
컨슈머는 다른 템플릿과 동일한 방식으로 템플릿을 실행합니다.
팁
컨슈머가 사용자 지정 코드로 클린룸을 설치할 때 마운트 오류가 발생하는 경우 코드에 구문 오류가 있다는 의미일 수 있습니다.
:ref:`공급자가 작성한 코드 예제 섹션<label-dcr_provider_written_code_examples>`에서 이 흐름을 보여주는 코드 예제를 찾을 수 있습니다.
버전 관리에 대한 중요 참고 사항¶
공급자는 함수를 업로드할 때마다 패치 번호를 증분합니다(패치 번호는 99로 제한됨). 따라서 개발하는 동안 버전 업데이트를 줄이기 위해 클린룸에 코드를 추가하기 전에 최선을 다해서 코드를 철저히 테스트하고 디버깅해야 합니다.
패치 번호를 업데이트하면 클린룸 UI를 사용하는 고객이 변경 사항을 확인하기 위해 페이지를 새로 고쳐야 할 수도 있습니다. API를 사용하는 고객에게는 변경 사항이 즉시 표시되지만, 사용 가능한 리소스에 따라 지연이 발생할 수 있습니다. 클린룸 버전 관리에 대해 자세히 알아보세요.
공급자가 작성한 인라인 함수 업로드¶
provider.load_python_into_cleanroom``의 ``code
매개 변수에서 코드를 인라인으로 업로드할 수 있습니다. 다음은 간단한 함수를 인라인으로 업로드하는 예입니다.
CALL samooha_by_snowflake_local_db.provider.load_python_into_cleanroom(
$cleanroom_name,
'simple_add', -- Name used to call the UDF from a template.
['first INTEGER', 'second INTEGER'], -- Arguments of the UDF, specified as '<variable_name> <SQL type>' pairs.
['numpy', 'pandas'], -- Packages imported by the UDF.
'INTEGER', -- SQL return type of UDF.
'add_two', -- Handler function in your code called when external name is called.
$$
import numpy as np # Not used, but you can load supported packages.
import pandas as pd
def add_two(first, second):
return first + second
$$
);
호출 템플릿은 ``cleanroom.simple_add``를 호출하여 이 함수를 호출합니다. :ref:`공급자 예제<label-dcr_provider_written_code_examples>`는 인라인 코드를 업로드하는 방법을 보여줍니다.
스테이지에서 공급자 작성 함수 업로드¶
Python 파일을 클린룸 스테이지에 업로드하고 ``provider.load_python_into_cleanroom``을 호출할 때 스테이지를 참조할 수 있습니다. 스테이지에서 코드를 로드하면 로컬 시스템의 편집기에서 코드를 개발하고, 인라인으로 로드할 때 복사/붙여넣기 오류를 방지하고, 더 효과적으로 버전 관리를 할 수 있습니다. 단일 프로시저 호출로 여러 파일을 업로드할 수 있지만, 업로드마다 하나의 처리기 함수만 노출됩니다.
``load_python_into_cleanroom``을 호출하면 코드가 스테이지에서 클린룸으로 로드됩니다. 나중에 스테이지의 코드 변경 사항이 클린룸으로 전파되지 않습니다.
스테이지로 UDF를 업로드하려면:
.py 파일을 만들고 Snowsight 스테이지에 업로드할 수 있는 위치에 저장합니다.
클린룸의 스테이지 이름을 가져오려면 ``provider.get_stage_for_python_files($cleanroom_name)``를 호출합니다. 이 스테이지는 클린룸에서 액세스할 수 있습니다. 하지만 본인이 만든 임의의 스테이지를 사용할 수 없습니다.
.py 파일을 클린룸의 스테이지에 업로드합니다. CLI, Snowsight 또는 언어별 드라이버를 사용하는 것을 포함하여 :doc:`이 작업을 수행하는 여러 가지 방법</user-guide/data-load-local-file-system-stage>`이 있습니다.
스테이지 위치, 처리기, 외부 이름, 인자 및 반환 유형을 지정하여 ``provider.load_python_into_cleanroom``을 호출합니다. 이제 클린룸의 템플릿에서 이 함수를 호출할 수 있습니다.
다음 예제 코드는 스테이지에서 클린룸으로 코드를 로드하는 방법을 보여줍니다.
-- Save the following code as reverser.py:
--import numpy as np
--def main(some_string):
-- '''Return the reverse of a string plus a random number 1-10'''
-- return some_string[::-1] + str(np.random.randint(1,10))
-- Get the stage for your clean room.
CALL samooha_by_snowflake_local_db.provider.get_stage_for_python_files($cleanroom_name);
-- Save the file to the stage. Here is how to do it by using the Snowflake CLI
PUT file://~/reverser.py <STAGE_NAME> overwrite=True auto_compress=False;
-- Load the code from the stage into the clean room.
CALL samooha_by_snowflake_local_db.provider.load_python_into_cleanroom(
$cleanroom_name,
'reverse', -- Name used to call the function
['some_string STRING'], -- Arguments and SQL types
['numpy'], -- Any required packages
['/reverser.py'], -- Relative path to file on stage
'STRING', -- Return type
'reverser.main' -- <FILE_NAME>.<FUNCTION_NAME>
);
-- Uploading code, even from a stage, increases the patch number.
CALL samooha_by_snowflake_local_db.provider.set_default_release_directive(
$cleanroom_name, 'V1_0', <NEW_PATCH_NUMBER>);
-- Upload a template that calls the function.
CALL samooha_by_snowflake_local_db.provider.add_custom_sql_template(
$cleanroom_name,
$udf_template_name,
$$
SELECT
p.status,
cleanroom.reverse(p.status)
FROM SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS AS p
LIMIT 100;
$$
);
-- Switch to the consumer account and run the template to see the results.
:ref:`공급자 예제<label-dcr_provider_written_code_examples>`는 스테이지에서 코드를 업로드하는 방법을 보여줍니다.
공급자가 작성한 코드 예제¶
다음 예제는 공급자가 작성한 UDFs 및 UDTFs를 클린룸에 추가하는 방법을 보여줍니다.
다음 예제를 다운로드한 후 Snowflake 계정에 워크시트 파일로 업로드합니다. 공급자와 컨슈머에 대해 각각 클린룸 API가 설치된 별도의 계정이 필요합니다. 샘플 파일에 나와 있는 것처럼 이 정보를 바꿉니다.
스테이지에서 파일 로드
. 공급자 예제를 실행한 후 이 노트북을 실행하여 스테이지에서 UDF를 로드해 봅니다.
컨슈머가 제출한 코드¶
컨슈머는 UDF 또는 UDTF 코드를 제출하고 사용자 지정 템플릿에서 호출할 수 있습니다. 컨슈머가 업로드한 코드는 사용자 지정 템플릿과 함께 단일 프로시저에 번들로 제공되며, 단일 프로시저 호출을 통해 업로드됩니다. 컨슈머 코드는 해당 템플릿에 직접 연결되어 있으므로, 다른 템플릿에서 호출할 수 없습니다.
컨슈머로서 코드를 업로드하려면 사용자 지정 템플릿 구문 및 :ref:`컨슈머 정의 템플릿을 제출하는 방법<label-dcr_consumer_written_templates>`을 이해해야 합니다.
컨슈머가 업로드한 모든 코드는 공급자가 업로드 권한을 요청할 때 볼 수 있습니다. 공급자 또는 컨슈머가 템플릿을 검사할 때마다 컨슈머 코드도 표시됩니다.
다음은 사용자 지정 컨슈머 코드를 업로드하는 단계의 개요입니다.
공급자가 표준 방식으로 클린룸을 만든 다음, 컨슈머를 초대합니다.
컨슈머가 표준 방식으로 클린룸을 설치하고 구성합니다.
컨슈머가 템플릿을 준비합니다. 이 템플릿은
cleanroom
네임스페이스 내에서 UDF 또는 UDTF를 호출합니다. 예를 들어, 컨슈머 정의calculate_tax
함수를 호출하려면 다음과 같은 코드 조각이 포함된 간단한 템플릿을 사용하면 됩니다.SELECT {{ cleanroom.calculate_tax(p.cost) }} AS Tax FROM my_db.my_sch.sales AS p;
컨슈머가 Python 코드를 준비합니다. 나중에 추가 이스케이프 처리가 필요하지 않도록 코드에 작은따옴표(
' '
) 대신 큰따옴표(" "
)를 사용하는 것이 좋습니다. 코드는 `선택한 Python 라이브러리 번들<https://repo.anaconda.com/pkgs/snowflake/>`_을 참조할 수 있습니다.컨슈머는 Python 코드를 ``consumer.generate_python_request_template``에 전달합니다. 이 프로시저는 사용자 지정 JinjaSQL 템플릿에 대한 자리 표시자와 함께 Python 코드를 저장 프로시저로 반환합니다. 이 템플릿에는 ``$$``를 여러 줄 구분 기호로 사용하는 여러 줄 문자열이 있습니다.
``generate_python_request_template``의 출력에서 템플릿 자리 표시자를 사용자의 JinjaSQL 템플릿으로 바꿉니다.
결합된 템플릿에서 ``'``와 같은 작은따옴표를 이스케이프합니다. 작은따옴표가 전체 여러 줄 프로시저 문자열의 가장 바깥쪽 구분 기호로 사용되기 때문입니다. 예:
BEGIN CREATE OR REPLACE FUNCTION CLEANROOM.custom_compare(min_status STRING, max_status STRING, this_status STRING) RETURNS boolean LANGUAGE PYTHON RUNTIME_VERSION = 3.10 PACKAGES = (\'numpy\') HANDLER = \'custom_compare\' AS $$ import numpy as np def custom_compare(min_status:str, max_status:str, this_status:str): statuses = [\'MEMBER\', \'SILVER\', \'GOLD\', \'PLATINUM\'] return ((statuses.index(this_status) >= statuses.index(min_status)) & (statuses.index(this_status) <= statuses.index(max_status))) $$; -- Custom template LET SQL_TEXT varchar := $$ SELECT c.status, c.hashed_email FROM IDENTIFIER( {{ my_table[0] }} ) as c WHERE cleanroom.custom_compare({{ min_status }}, {{ max_status }}, c.status); $$; LET RES resultset := (EXECUTE IMMEDIATE :SQL_TEXT); RETURN TABLE(RES); END;
결합된 템플릿을 사용하여
consumer.create_template_request``를 호출합니다. ``template_definition
인자에서 저장 프로시저용 코드 주위에 이중 달러 구분 기호($$...$$
) 대신 작은따옴표(' '
)를 사용합니다. 예:CALL samooha_by_snowflake_local_db.consumer.create_template_request( $cleanroom_name, $template_name, ' BEGIN -- First, define the Python UDF. CREATE OR REPLACE FUNCTION CLEANROOM.custom_compare(min_status STRING, max_status STRING, this_status STRING) RETURNS boolean LANGUAGE PYTHON RUNTIME_VERSION = 3.10 PACKAGES = (\'numpy\') HANDLER = \'custom_compare\' AS $$ import numpy as np def custom_compare(min_status:str, max_status:str, this_status:str): statuses = [\'MEMBER\', \'SILVER\', \'GOLD\', \'PLATINUM\'] return ((statuses.index(this_status) >= statuses.index(min_status)) & (statuses.index(this_status) <= statuses.index(max_status))) $$; -- Then define and execute the SQL query. LET SQL_TEXT varchar := $$ SELECT c.status, c.hashed_email FROM IDENTIFIER( {{ my_table[0] }} ) as c WHERE cleanroom.custom_compare({{ min_status }}, {{ max_status }}, c.status); $$; -- Execute the query and then return the result. LET RES resultset := (EXECUTE IMMEDIATE :SQL_TEXT); RETURN TABLE(RES); END; ');
컨슈머와 공급자가 표준 :ref:`컨슈머 정의 템플릿 흐름<label-dcr_consumer_written_templates>`을 계속 유지합니다.
공급자가 템플릿 요청을 확인한 다음(
provider.list_pending_template_requests
), ``approve_template_request``를 호출하여 승인합니다. 공급자는 요청에서 템플릿과 번들 코드를 볼 수 있습니다.컨슈머가 요청 상태를 확인하고(
consumer.list_template_requests
) 상태가 APPROVED인 경우 템플릿을 실행합니다(consumer.run_analysis
).
컨슈머가 작성한 코드 예제¶
다음 예제에서는 공급자가 작성한 UDFs를 클린룸에 추가하는 방법을 보여줍니다.
다음 예제를 다운로드한 후 Snowflake 계정에 워크시트 파일로 업로드합니다. 공급자와 컨슈머에 대해 각각 클린룸 API가 설치된 별도의 계정이 필요합니다. 샘플 파일에 나와 있는 것처럼 이 정보를 바꿉니다.