Snowflake Data Clean Rooms: 보안 Snowpark 프로시저¶
이 항목에서는 클린룸을 프로그래밍 방식으로 설정하고 이를 컨슈머와 공유하고, 공급자 계정에서 클린룸에 로드된 보안 Snowpark 절차를 사용하여 분석을 실행하는 데 필요한 공급자 및 컨슈머 흐름을 설명합니다. 이 흐름에서 공급자는 기본 Python 코드를 컨슈머로부터 완전히 기밀로 유지하는 API를 사용하여 보안 Snowpark 프로시저를 클린룸에 로드합니다.
이 흐름의 Snowpark 프로시저는 노출 수에 대한 도달률의 선형 회귀를 수행하여 기울기를 추정합니다. 공급자의 계정에 대한 노출 IDs, 사용자 IDs 및 타임스탬프가 있는 테이블과 선택적으로 컨슈머의 사용자 테이블을 입력으로 받습니다. Snowpark 프로시저는 노출 데이터가 제공되면 이를 컨슈머의 사용자 데이터에 조인하기 위해 SQL을 동적으로 생성하고, 클린룸에 일별 노출 수와 도달 범위가 포함된 중간 테이블을 생성합니다.
다음으로, 중간 테이블의 데이터는 Snowpark 프로시저 내부에서 처리되고, 회귀 분석을 통해 절편, 기울기 및 기타 여러 매개 변수를 추정합니다. 이 데이터는 클린룸 내부의 결과 테이블에 기록되고, 이 테이블의 ID는 컨슈머에게 출력으로 제공됩니다. 마지막으로, 컨슈머는 이 ID와 함께 get_results 템플릿을 사용하여 클린룸에서 데이터를 다시 가져올 수 있습니다. Snowpark 프로시저가 완료되기 전에 클린룸에서 생성된 모든 중간 테이블을 정리합니다.
참고: 모든 중간 테이블은 클린룸 내부에 생성되므로, Snowpark 프로시저 담당자 외에는 누구도 액세스할 수 없습니다.
위에 언급된 것 외에 이 흐름의 주요 측면은 다음과 같습니다.
공급자:
a. 클린룸에 Snowpark 프로시저를 안전하게 추가합니다.
b. Snowpark 프로시저를 실행하는 사용자 지정 템플릿과 결과를 검색하는 또 다른 템플릿을 추가합니다.
c. 컨슈머와 클린룸을 공유합니다.
컨슈머:
a. 회귀를 수행하는 템플릿을 실행합니다.
b. 분석 결과를 검색합니다.
전제 조건¶
이 흐름을 완료하려면 두 개의 별도 Snowflake 계정이 필요합니다. 첫 번째 계정을 사용하여 공급자의 명령을 실행한 다음 두 번째 계정으로 전환하여 컨슈머의 명령을 실행합니다.
공급자¶
참고
다음 명령은 공급자 계정의 Snowflake 워크시트에서 실행해야 합니다.
환경 설정¶
개발자 APIs를 사용하여 Snowflake Data Clean Room으로 작업하기 전에 다음 명령을 실행하여 Snowflake 환경을 설정합니다. SAMOOHA_APP_ROLE 역할이 없는 경우 계정 관리자에게 문의하십시오.
use role samooha_app_role;
use warehouse app_wh;
클린룸 만들기¶
클린룸의 이름을 지정합니다. 기존 클린룸 이름과 충돌하지 않도록 새 클린룸 이름을 입력합니다. 클린룸 이름은 영숫자 만 가능합니다. 클린룸 이름에는 공백과 밑줄 외의 특수문자는 포함될 수 없습니다.
set cleanroom_name = 'Snowpark Demo clean room';
위에서 설정한 클린룸 이름으로 새로운 클린룸을 만들 수 있습니다. 위에 설정한 클린룸 이름이 기존 클린룸으로 이미 존재하는 경우 이 프로세스는 실패합니다.
이 절차의 실행에는 약 45초가 걸립니다.
provider.cleanroom_init 의 두 번째 인자는 클린룸의 분포입니다. 이는 INTERNAL 또는 EXTERNAL일 수 있습니다. 테스트 목적으로 클린룸을 같은 조직의 계정과 공유하는 경우 INTERNAL을 사용하여 애플리케이션 패키지를 공동 작업자에게 릴리스하기 전에 수행해야 하는 자동화된 보안 검사를 우회할 수 있습니다. 그러나 이 클린룸을 다른 조직의 계정과 공유하는 경우에는 EXTERNAL 클린룸 배포를 사용해야 합니다.
call samooha_by_snowflake_local_db.provider.cleanroom_init($cleanroom_name, 'INTERNAL');
보안 검사 상태를 보려면 다음을 사용합니다.
call samooha_by_snowflake_local_db.provider.view_cleanroom_scan_status($cleanroom_name);
클린룸을 만든 후에는 공동 작업자와 공유하기 전에 릴리스 지시문을 설정해야 합니다. 그러나 배포가 EXTERNAL로 설정된 경우에는 먼저 보안 검사가 완료될 때까지 기다린 후 릴리스 지시문을 설정해야 합니다. 나머지 단계를 계속 실행하고 스캔이 실행되는 동안 provider.create_cleanroom_listing 단계 전에 여기로 돌아올 수 있습니다.
릴리스 지시문을 설정하려면 다음을 호출합니다.
call samooha_by_snowflake_local_db.provider.set_default_release_directive($cleanroom_name, 'V1_0', '0');
리전 간 공유¶
사용자의 계정과 다른 리전에 있는 Snowflake 고객과 클린룸을 공유하려면 클라우드 간 자동 복제를 활성화해야 합니다. 다른 리전의 컨슈머와 협업하는 데 따른 추가 비용에 대한 자세한 내용은 클라우드 간 자동 복제 비용을 참조하십시오.
개발자 APIs를 사용할 때, 리전 간 공유를 활성화하는 것은 2단계 프로세스입니다.
ACCOUNTADMIN 역할이 있는 Snowflake 관리자는 Snowflake 계정에 대해 클라우드 간 자동 복제를 사용 설정할 수 있습니다. 자세한 지침은 다른 리전의 계정과 협업을 참조하십시오.
provider.enable_laf_for_cleanroom 명령을 실행하여 클린룸에 클라우드 간 자동 복제를 사용하도록 설정합니다. 예:
call samooha_by_snowflake_local_db.provider.enable_laf_for_cleanroom($cleanroom_name);
클린룸에 대해 클라우드 간 자동 복제를 활성화한 후에는 평소와 같이 provider.create_cleanroom_listing 명령을 사용하여 목록에 컨슈머를 추가할 수 있습니다. 목록은 필요에 따라 원격 클라우드 및 리전에 자동으로 복제됩니다.
데이터 세트를 연결하고 데이터 세트에 대한 조인 정책을 설정합니다.¶
클린룸에 Snowflake 테이블을 연결합니다. 이러한 데이터 세트는 최신 패치를 통해 사용자의 계정에서 사용할 수 있게 됩니다. Snowflake 계정의 테이블 목록을 탐색하고 정규화된 테이블 이름(Database.Schema.Table)을 배열로 입력합니다. 이 절차를 사용하면 클린룸 내부에서 테이블을 안전하게 볼 수 있어 테이블에 자동으로 액세스할 수 있으므로 테이블 사본을 만들 필요가 없습니다.
call samooha_by_snowflake_local_db.provider.link_datasets($cleanroom_name, ['<IMPRESSIONS_TABLE>']);
대신 다운스트림 종속성이 있는 클린룸에 뷰를 연결하려면, provider.link_datasets_advanced 를 대신 사용합니다.
call samooha_by_snowflake_local_db.provider.link_datasets_advanced($cleanroom_name, ['<VIEW_NAME>'], ['<SOURCE_DB_NAMES>']);
참고
테이블이 있지만, 이 단계가 작동하지 않는다면 SAMOOHA_APP_ROLE 역할에 아직 테이블에 대한 액세스 권한이 부여되지 않았기 때문일 수 있습니다. 그러한 경우, ACCOUNTADMIN 역할로 전환하고 데이터베이스에서 아래 프로시저를 호출한 다음 다시 전환하여 나머지 흐름을 진행합니다.
use role accountadmin;
call samooha_by_snowflake_local_db.provider.register_db('<DATABASE_NAME>');
use role samooha_app_role;
다음 절차에 따라 클린룸에 연결된 데이터 세트를 볼 수 있습니다.
call samooha_by_snowflake_local_db.provider.view_provider_datasets($cleanroom_name);
조인 정책으로 사용할 열을 파악하기 위해 데이터 집합을 들여다보고 PII 열을 결정할 수 있습니다. 상위 10개 행을 보려면 다음 쿼리를 사용합니다.
select * from <IMPRESSIONS_TABLE> limit 10;
Snowpark 프로시저를 클린룸에 기밀로 로드합니다.¶
이 섹션에서는 Snowpark 프로시저를 클린룸에 적용하는 방법을 보여줍니다. 이 프로시저는 다음 단계로 진행됩니다.
노출 데이터 전처리: 컨슈머의 테이블이 제공되면 공급자 노출 데이터를 컨슈머 사용자 데이터에 조인하고, 날짜별 노출 수와 도달 수를 계산해 클린룸 내부의 중간 테이블에 저장하는 동적 SQL이 생성됩니다. 컨슈머 테이블이 제공되지 않으면 공급자의 노출 테이블의 엔터티가 사용됩니다.
중간 테이블 로드: 중간 테이블은 Snowpark 프로시저에 pandas 데이터프레임으로 로드됩니다.
회귀 수행: 회귀는 statsmodels 라이브러리를 사용하여 계산되고 결과는 pandas 데이터프레임으로 반환됩니다.
Snowflake 테이블에 결과 쓰기: 클린룸 내부의 결과 테이블에 결과가 기록되며, 테이블의 ID 접미사는 컨슈머에게 반환됩니다.
a. Snowpark 프로시저는 클린룸 내부에서 실행되기 때문에 컨슈머 테넌트에게 직접 쓸 수 있는 기능이 제한되어 있습니다. 대신, 결과를 더 안전하게 유지하기 위해 클린룸 내부의 테이블에 기록하여 컨슈머가 테이블에서 읽을 수 있도록 합니다.
중간 테이블 삭제: 클린룸 내부에서 계산하는 동안 생성되어 더 이상 필요 없는 중간 테이블은 Snowpark 프로시저가 완료되기 전에 삭제됩니다.
다음 API를 사용하면 Python 함수를 클린룸에 인라인 함수로 직접 정의할 수 있습니다. 또는 클린룸 스테이지에 업로드한 스테이징된 파일에서 Python을 로드할 수 있습니다. 예제는 API 참조 가이드를 참조하십시오.
call samooha_by_snowflake_local_db.provider.load_python_into_cleanroom(
$cleanroom_name,
'reach_impression_regression',
['source_table string', 'my_table string'],
['snowflake-snowpark-python', 'pandas', 'statsmodels', 'numpy'],
'string',
'main',
$$
import traceback
import pandas as pd
import numpy as np
import statsmodels.formula.api as sm
def drop_tables(session, table_names):
"""
Drop the tables passed in
"""
for tbl in table_names:
session.sql(f'drop table {tbl}').collect()
def preprocess_regression_data(session, source_table, my_table, suffix):
"""
Preprocess the impressions and customer data into an intermediary table for regression
"""
table_name = f'cleanroom.intermediary_{suffix}'
my_table_statement = f'inner join {my_table} c on p.hem = c.hem' if my_table != 'NONE' else ''
session.sql(f"""
create or replace table {table_name} as (
with joint_data as (
select
date,
p.hem as hem,
impression_id
from {source_table} p
{my_table_statement}
)
select
date,
count(distinct hem) as reach,
count(distinct impression_id) as num_impressions
from joint_data
group by date
order by date
);
""").collect()
return table_name
def calculate_regression(df):
"""
Calculate the regression data from the dataframe we put together
"""
result = sm.ols('REACH ~ 1 + NUM_IMPRESSIONS', data=df).fit()
retval = pd.concat([
result.params,
result.tvalues,
result.pvalues
], keys=['params', 't-stat', 'p-value'], names=['STATISTIC', 'PARAMETER']).rename('VALUE').reset_index()
return retval
def main(session, source_table, my_table):
"""
First compute the regression data from an overlap between customer and provider data, and counting
the number of impressions and reach per day. Next regress these locally and compute the regression
statistics. Finally write it to a results table which can be queried to get the output.
"""
suffix = f'regression_results_{abs(hash((source_table, my_table))) % 10000}'
try:
# Preprocess impressions and customer data into an intermediary form to use for regression
intermediary_table_name = preprocess_regression_data(session, source_table, my_table, suffix)
# Load the data into Python locally
df = session.table(intermediary_table_name).to_pandas()
# Carry out the regression and get statistics as an output
regression_output = calculate_regression(df)
# Write the statistics to an output table
# The table and the schema names should be in upper case to quoted identifier related issues.
table = f'results_{suffix}'.upper()
retval_df = session.write_pandas(regression_output, table, schema = 'CLEANROOM', auto_create_table = True)
# Drop any intermediary tables
drop_tables(session, [intermediary_table_name])
# Tell the user the name of the table the results have been written to
return f'Done, results have been written to the following suffix: {suffix}'
except:
return traceback.format_exc()
$$
);
참고
클린룸에 Python을 로드하면 클린룸에 새로운 패치가 생성됩니다. 클린룸 배포가 EXTERNAL로 설정된 경우 보안 검사가 완료될 때까지 기다린 다음 다음을 사용하여 기본 릴리스 지시문을 업데이트해야 합니다.
-- See the versions available inside the clean room
show versions in application package samooha_cleanroom_Snowpark_Demo_clean_room;
-- Once the security scan is approved, update the release directive to the latest version
call samooha_by_snowflake_local_db.provider.set_default_release_directive($cleanroom_name, 'V1_0', '1');
UDFs를 사용하여 사용자 지정 템플릿 추가¶
클린룸에 사용자 지정 분석 템플릿을 추가하려면 공급자 측과 컨슈머 측 모두에 테이블 이름을 위한 자리표시자와 공급자 측의 조인 열이 필요합니다. SQL Jinja 템플릿에서는 다음 자리 표시자가 항상 있어야 합니다.
source_table: 공급자의 테이블 이름 배열
my_table: 컨슈머의 테이블 이름 배열
이러한 변수를 사용하여 테이블 이름을 동적으로 만들 수 있지만, 원하는 경우 클린룸에 연결된 뷰의 이름을 사용하여 템플릿에 하드코딩할 수도 있습니다. 원하는 경우 열 이름을 템플릿에 하드코딩하거나 매개 변수를 통해 동적으로 설정할 수 있습니다. 매개 변수를 통해 설정한 경우 열 정책에 대해 확인하려면 배열이어야 하는 매개 변수 dimensions 또는 measure_column 을 호출해야 한다는 점을 기억하십시오. 이를 템플릿에 SQL Jinja 매개 변수로 추가하면 나중에 컨슈머가 쿼리할 때 전달할 수 있습니다. 조인 정책은 컨슈머가 권한이 있는 열 이외의 열에 조인할 수 없도록 보장합니다.
또는 사용자 지정 SQL Jinja 템플릿의 모든 인자가 다음 필터를 사용하여 조인 및 열 정책을 준수하는지 확인할 수 있습니다.
join_policy: 문자열 값 또는 필터 절이 조인 정책을 준수하는지 확인합니다.
column_policy: 문자열 값 또는 필터 절이 열 정책을 준수하는지 확인합니다.
join_and_column_policy: 필터 절에서 조인에 사용된 열이 조인 정책을 준수하는지, 필터로 사용된 열이 열 정책을 준수하는지 확인합니다.
예를 들어, {{ provider_id | sqlsafe | join_policy }} 절에서, p.HEM 의 입력을 구문 분석하여 p.HEM 이 조인 정책에 있는지 확인합니다. 참고: sqlsafe 필터는 공동 작업자가 템플릿에 순수 SQL을 삽입할 수 있도록 허용하므로 주의해서 사용해야 합니다.
참고
모든 공급자/컨슈머 테이블은 이러한 인자를 사용하여 참조되어야 합니다. 왜냐하면 클린룸에 실제로 연결된 보안 뷰의 이름이 테이블 이름과 다르기 때문입니다. 중요한 사항으로, 공급자 테이블 별칭은 반드시 p(또는 p1), p2, p3, p4 등이어야 하며, 컨슈머 테이블 별칭은 반드시 c(또는 c1), c2, c3 등이어야 합니다. 이는 클린룸의 보안 정책을 시행하기 위해 필요합니다.
이 함수는 이름이 동일한 기존 템플릿을 모두 재정의합니다. 기존 템플릿을 업데이트하려면 업데이트된 템플릿으로 이 함수를 다시 호출하면 하면 됩니다.
call samooha_by_snowflake_local_db.provider.add_custom_sql_template(
$cleanroom_name,
'prod_calculate_regression',
$$
call cleanroom.reach_impression_regression({{ source_table[0] }}, {{ my_table[0] | default('NONE') }});
$$
);
마지막으로, 계산_회귀 템플릿에서 반환된 결과 접미사 ID가 주어지면 컨슈머가 분석 결과를 검색할 수 있는 사용자 지정 템플릿이 추가됩니다.
call samooha_by_snowflake_local_db.provider.add_custom_sql_template(
$cleanroom_name,
'prod_get_results',
$$
select * from cleanroom.results_{{ results_suffix | sqlsafe }};
$$
);
현재 클린룸에서 활성화된 템플릿을 보려면 다음 프로시저를 호출합니다.
call samooha_by_snowflake_local_db.provider.view_added_templates($cleanroom_name);
컨슈머¶
참고
다음 명령은 컨슈머 계정의 Snowflake 워크시트에서 실행해야 합니다.
환경 설정¶
개발자 APIs를 사용하여 Snowflake Data Clean Room으로 작업하기 전에 다음 명령을 실행하여 Snowflake 환경을 설정합니다. SAMOOHA_APP_ROLE 역할이 없는 경우 계정 관리자에게 문의하십시오.
use role samooha_app_role;
use warehouse app_wh;
클린룸 설치¶
클린룸 공유가 설치되면 아래 명령을 사용하여 사용 가능한 클린룸 목록을 볼 수 있습니다.
call samooha_by_snowflake_local_db.consumer.view_cleanrooms();
공급자가 사용자와 공유한 클린룸의 이름을 지정합니다.
set cleanroom_name = 'Snowpark Demo clean room';
다음 명령은 연결된 공급자와 선택된 클린룸을 사용하여 컨슈머 계정에 클린룸을 설치합니다.
이 절차의 실행에는 약 45초가 걸립니다.
call samooha_by_snowflake_local_db.consumer.install_cleanroom($cleanroom_name, '<PROVIDER_ACCOUNT_LOCATOR>');
클린룸이 설치되면 공급자는 클린룸을 사용하기 전에 공급자 측에서 클린룸 설치를 완료해야 합니다. 아래 함수을 사용하면 클린룸의 상태를 확인할 수 있습니다. 해당 기능이 활성화되면 아래의 Run Analysis 명령을 실행할 수 있습니다. 일반적으로 클린룸이 활성화되려면 약 1분이 걸립니다.
이 함수를 실행하기 전에 install_cleanroom 함수가 완료되었는지 확인합니다.
call samooha_by_snowflake_local_db.consumer.is_enabled($cleanroom_name);
데이터 세트 연결¶
공급자의 데이터를 사용하여 보안 계산을 수행하기 위해 클린룸에 데이터 세트를 연결합니다. 이러한 데이터 세트는 최신 패치를 통해 사용자의 계정에서 사용할 수 있게 됩니다.
call samooha_by_snowflake_local_db.consumer.link_datasets($cleanroom_name, ['<USERS_TABLE>']);
참고
테이블이 있지만, 이 단계가 작동하지 않는다면 SAMOOHA_APP_ROLE 역할에 아직 테이블에 대한 액세스 권한이 부여되지 않았기 때문일 수 있습니다. 그러한 경우, ACCOUNTADMIN 역할로 전환하고 데이터베이스에서 아래 프로시저를 호출한 다음 다시 전환하여 나머지 흐름을 진행합니다.
use role accountadmin;
call samooha_by_snowflake_local_db.consumer.register_db('<DATABASE_NAME>');
use role samooha_app_role;
분석을 실행하려면 컨슈머 테이블을 전달해야 합니다. 클린룸에 추가한 데이터 세트를 보려면 다음 프로시저를 호출합니다.
call samooha_by_snowflake_local_db.consumer.view_consumer_datasets($cleanroom_name);
분석 실행¶
이제 클린룸이 설치되었으므로 “run_analysis” 명령을 사용하여 공급자가 클린룸에 제공한 분석 템플릿을 실행할 수 있습니다. 아래 섹션에서 각 필드가 어떻게 결정되는지 확인할 수 있습니다.
전달 가능한 데이터 세트의 수는 공급자가 구현한 템플릿에 따라 제한됩니다. 일부 템플릿에는 특정 개수의 테이블이 필요합니다. 템플릿 작성자는 지원할 요구 사항을 구현할 수 있습니다.
참고
분석을 실행하기 전에 웨어하우스 크기를 변경하거나, 대규모 테이블의 경우 더 크고 새로운 웨어하우스 크기를 사용할 수 있습니다.
call samooha_by_snowflake_local_db.consumer.run_analysis(
$cleanroom_name, -- cleanroom
'prod_calculate_regression', -- template name
['<USERS_TABLE>'], -- consumer tables
['<IMPRESSSIONS_TABLE>'], -- provider tables
object_construct() -- Rest of the custom arguments needed for the template
);
이 분석의 출력은 다음 템플릿을 사용하여 회귀 결과를 검색하는 데 사용할 수 있는 ID입니다.
set result_suffix = 'regression_results_<ID>';
call samooha_by_snowflake_local_db.consumer.run_analysis(
$cleanroom_name, -- cleanroom
'prod_get_results', -- template name
[], -- consumer tables
[], -- provider tables
object_construct(
'results_suffix', $result_suffix -- The suffix with the results
)
);
run_analysis에 대한 입력을 결정하는 방법¶
분석을 실행하려면 run_analysis 함수에 몇 가지 매개 변수를 전달해야 합니다. 이 섹션에서는 전달할 매개 변수를 결정하는 방법을 설명합니다.
템플릿 이름
먼저, 다음 프로시저를 호출하여 지원되는 분석 템플릿을 볼 수 있습니다.
call samooha_by_snowflake_local_db.consumer.view_added_templates($cleanroom_name);
분석을 실행하려면 run_analysis 함수에 몇 가지 매개 변수를 전달해야 합니다. 이 섹션에서는 전달할 매개 변수를 결정하는 방법을 설명합니다.
call samooha_by_snowflake_local_db.consumer.view_template_definition($cleanroom_name, 'prod_calculate_regression');
여기에는 많은 수의 다양한 SQL Jinja 매개 변수가 포함되는 경우가 많습니다. 다음 기능은 SQL Jinja 템플릿의 구문을 분석하고 run_analysis에 지정해야 하는 인자를 목록으로 추출합니다.
call samooha_by_snowflake_local_db.consumer.get_arguments_from_template($cleanroom_name, 'prod_calculate_regression');
데이터 세트 이름
공급자가 클린룸에 추가한 데이터 세트 이름을 보려면 다음 프로시저를 호출합니다. 클린룸의 보안 속성으로 인해 공급자가 클린룸에 추가한 데이터 세트에 있는 데이터를 볼 수 없습니다.
call samooha_by_snowflake_local_db.consumer.view_provider_datasets($cleanroom_name);
다음 호출을 사용하면 클린룸에 연결한 테이블도 볼 수 있습니다.
call samooha_by_snowflake_local_db.consumer.view_consumer_datasets($cleanroom_name);
권장 사항¶
가능한 경우 모든 중요한 데이터 전처리는 동적 SQL을 통해 수행되며, 클린룸 스키마를 사용하여 중간 테이블에 데이터를 저장합니다. 이 방법이 훨씬 빠르고 효율적입니다. 예:
session.sql("create or replace table cleanroom.intermediary as ...")
Snowpark 데코레이터를 사용하지 않고 클린룸 스키마에서 session.sql을 통해 SQL를 실행하여 UDFs, UDTFs 및 프로시저를 생성합니다. 예:
session.sql("create or replace function cleanroom.udf(...")
메모리 용량을 초과하는 대규모 데이터를 로드해야 하는 경우, .to_pandas_batches()를 사용하여 반복 처리합니다. 예:
df_iter = session.table(intermediary_table_name).to_pandas_batches() for df in df_iter: ...