데이터브릭스, 스파크 구조화 스트리밍에 온디맨드 상태 리파티셔닝 기능 추가
데이터브릭스가 체크포인트를 다시 만들지 않고도 구조화된 스트리밍(Structured Streaming) 쿼리의 파티션 수를 변경할 수 있는 퍼블릭 프리뷰 기능을 발표했다.
한 줄 답
{"duration_api_ms":2690,"stop_reason":"end_turn","session_id":"4e8fb5bd-b69f-4564-87aa-a7a850e02691","total_cost_usd":0.0034362000000000004,"usage":{"input_tokens":2,"cache_creation_input_tokens":0,"cache_read_input_tokens":9031,"output_tokens":45,"output_tokens_details":{"thinking_tokens":0},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":2,"output_tokens":45,"cache_read_input_tokens":9031,"cache_creation_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"modelUsage":{"claude-haiku-4-5-20251001":{"inputTokens":1071,"outputTokens":21,"cacheReadInputTokens":0,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.001176,"contextWindow":200000,"maxOutputTokens":32000,"thinkingTokens":0,"canonicalModel":"claude-haiku-4-5","provider":"firstParty","costBasis":"list"},"claude-sonnet-5":{"inputTokens":2,"outputTokens":45,"cacheReadInputTokens":9031,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.0022602000000000004,"contextWindow":1000000,"maxOutputTokens":64000,"thinkingTokens":0,"canonicalModel":"claude-sonnet-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"Databricks는 Apache Spark Structured Streaming의 상태 관리와 관련해 무엇을 발표했나요?","ttft_ms":2757,"type":"result","duration_ms":2842,"uuid":"6b1dd9b4-8f7d-432a-88c6-4f4504ccdfcc","ttft_stream_ms":2419,"time_to_request_ms":1146,"queued_turn_count":0} Client.listTools() called but server does not advertise tools capability - returning empty list
Databricks가 스테이트풀(stateful) 구조적 스트리밍(Structured Streaming) 쿼리의 파티션 수를 체크포인트 상태를 잃거나 재구축하지 않고도 새로운 설정으로 재시작해 변경할 수 있는 기능인 온디맨드 상태 리파티셔닝(on-demand state repartitioning)을 발표했다. 이 기능은 Databricks Runtime 18 이상에서 퍼블릭 프리뷰(Public Preview)로 제공된다.
핵심 요약
- Databricks가 아파치 스파크 구조적 스트리밍(Apache Spark Structured Streaming)을 위한 온디맨드 상태 리파티셔닝(on-demand state repartitioning) 기능을 발표했다. 이 기능은 현재 Databricks Runtime 18 이상에서 퍼블릭 프리뷰(Public Preview)로 제공된다.
- The feature lets users change a stateful streaming query's partition count via the new spark.sql.streaming.stateStore.partitions setting, which takes precedence over spark.sql.shuffle.partitions for stateful queries.
- 이전에는 체크포인트 생성 시 파티션 수가 고정되어, 체크포인트를 폐기하고 누적된 상태를 잃지 않는 한 spark.sql.shuffle.partitions를 변경해도 아무런 효과가 없었다.
- Databricks에 따르면 필요한 요구 사항은 Databricks Runtime 18 이상과 RocksDB 상태 저장소 제공자이며, 이는 DBR 17.3 이상에서 기본값으로 설정되어 있다.
- 데이터브릭스에 따르면, 초기 도입 기업인 코베오(Coveo)는 이 기능을 도입한 후 관련 아마존 S3 API 비용을 40% 절감했다. 이는 발표문에 포함된 코베오의 알렉시스 시코인(Alexis Chicoine)의 발언에서 인용된 내용이다.
Databricks가 아파치 스파크 구조화 스트리밍(Apache Spark Structured Streaming)을 위한 온디맨드 상태 리파티셔닝(on-demand state repartitioning) 기능을 발표했다. 이는 체크포인트를 재구성하거나 누적된 상태를 잃지 않고도 스테이트풀 스트리밍 쿼리의 파티션 수를 조정할 수 있게 해주는 퍼블릭 프리뷰(Public Preview) 기능이다. 이 기능은 Databricks Runtime 18 이상에서 사용할 수 있다.
변경 사항
Databricks에 따르면, 스테이트풀 스트리밍 쿼리는 그동안 체크포인트가 생성되는 시점에 파티션 수가 고정되는 구조였다. 표준 설정인 spark.sql.shuffle.partitions 값을 변경하고 쿼리를 재시작해도 아무런 효과가 없었는데, 파티션 구조가 이미 체크포인트에 반영되어 있었기 때문이다. Databricks는 기존에는 체크포인트를 폐기하고 새로 시작하는 방법밖에 없었으며, 이 경우 수백만 개 계정에 걸친 사기 탐지 데이터나 며칠에 걸친 세션 윈도우처럼 쿼리가 축적해온 모든 상태를 잃게 됐다고 설명했다.
Databricks는 온디맨드 상태 리파티셔닝이 이러한 제약을 없앤다고 밝혔다. 이 기능은 집계, 스트림 간 조인(stream-stream join), 중복 제거, 세션화, transformWithState 워크로드를 포함한 모든 스테이트풀 스트리밍 쿼리에 적용된다.
작동 방식
Databricks에 따르면 사용자는 전용 설정값인 spark.sql.streaming.stateStore.partitions를 지정한 뒤 쿼리를 재시작하면 된다. 이 설정은 스테이트풀 쿼리에서 spark.sql.shuffle.partitions보다 우선 적용된다. 재시작 시 쿼리는 먼저 대기 중인 마이크로배치를 완료한 다음, 새로운 파티션 수에 맞춰 상태 데이터를 물리적으로 재분배하고 키를 올바른 위치로 재해싱하는 일회성 리파티션 작업을 수행한다. 이 작업이 끝나면 쿼리는 새로운 파티션 수를 기준으로 처리를 재개한다.
Databricks는 이 기능을 사용하려면 Databricks Runtime 18 이상과 RocksDB 상태 저장소 제공자(state store provider)가 필요하며, 이는 DBR 17.3 이상에서 새로 생성되는 쿼리의 기본값이라고 밝혔다.
모니터링
Databricks에 따르면 리파티션 작업의 소요 시간은 표준 StreamingQueryProgress 이벤트에 표시되며, 구체적으로는 durationMs 지표 아래 controlBatch.REPARTITION이라는 필드에 나타난다. 회사 측은 리파티션 소요 시간이 관련 상태의 양에 비례하지만, 대부분의 워크로드에서는 단 몇 초밖에 걸리지 않을 것으로 예상한다고 밝혔다.
제시된 활용 사례
Databricks는 이 기능의 세 가지 활용 시나리오를 제시했다. 첫째는 출시 이후 쿼리 규모를 재조정하는 경우로, 소규모 파일럿을 위해 설정했던 초기 파티션 수가 이후 성장세를 따라가지 못할 때다. 예를 들어 사기 점수 산정 스트림이 한 지역에서 여러 지역으로 확장되는 경우다. 둘째는 변화하는 워크로드에 맞춘 조정으로, 광고 입찰 파이프라인이 주간 트래픽에는 파티션을 늘리고 야간에는 줄이는 식이다. 셋째는 과거 데이터를 백필(backfill)하는 경우로, 과거 데이터를 재처리하는 속도를 높이기 위해 일시적으로 파티션 수를 늘렸다가 정상 트래픽 상태로 돌아오면 다시 줄이는 방식이다.
고객 사례
Databricks는 이 기능의 초기 도입 기업인 Coveo의 시니어 소프트웨어 개발자 Alexis Chicoine의 발언을 인용했다. Chicoine은 Coveo가 데이터 양이 크게 변동하는 대규모 스테이트풀 스트리밍 파이프라인을 운영하고 있으며, 이 기능을 통해 관련 Amazon S3 API 비용을 40% 절감했다고 밝혔다. 그는 이전에는 과잉 프로비저닝을 하거나 새 체크포인트에서 다시 구축하는 것 중 하나를 선택해야 했고, 이로 인해 스토리지 API 비용이 컴퓨팅 비용에 근접할 정도로 늘어났었다며, 이제는 기존 상태를 방해하거나 체크포인트 마이그레이션을 유발하지 않고도 규모를 조정할 수 있게 됐다고 말했다.
출처: Databricks 블로그, "Announcing On-Demand State Repartitioning for Apache Spark™ Structured Streaming on Databricks," 2026년 9월 14일 게재.
자주 묻는 질문
- 온디맨드 상태 리파티셔닝이란 무엇인가?
- 이는 Databricks가 제공하는 퍼블릭 프리뷰 기능으로, 상태 기반(stateful) Apache Spark 구조적 스트리밍(Structured Streaming) 쿼리를 중지했다가 다시 시작하는 방식으로 상태 파티션 수를 변경할 수 있게 해주며, 이 과정에서 기존 체크포인트 상태는 그대로 유지된다.
- {"duration_api_ms":2889,"stop_reason":"end_turn","session_id":"c8114d9b-ed2b-4874-8ad5-34588a0192d9","total_cost_usd":0.0031138,"usage":{"input_tokens":2,"cache_creation_input_tokens":0,"cache_read_input_tokens":9014,"output_tokens":19,"output_tokens_details":{"thinking_tokens":0},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":2,"output_tokens":19,"cache_read_input_tokens":9014,"cache_creation_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"modelUsage":{"claude-haiku-4-5-20251001":{"inputTokens":1062,"outputTokens":11,"cacheReadInputTokens":0,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.001117,"contextWindow":200000,"maxOutputTokens":32000,"thinkingTokens":0,"canonicalModel":"claude-haiku-4-5","provider":"firstParty","costBasis":"list"},"claude-sonnet-5":{"inputTokens":2,"outputTokens":19,"cacheReadInputTokens":9014,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.0019968,"contextWindow":1000000,"maxOutputTokens":64000,"thinkingTokens":0,"canonicalModel":"claude-sonnet-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"이용하려면 무엇이 필요한가요?","ttft_ms":2714,"type":"result","duration_ms":2852,"uuid":"d9a3a7af-6d08-4535-b945-4ae7f50138bb","ttft_stream_ms":2432,"time_to_request_ms":980,"queued_turn_count":0} Client.listTools() called but server does not advertise tools capability - returning empty list
- 데이터브릭스에 따르면 Databricks Runtime 18 이상과 RocksDB 상태 저장소 제공자가 필요하며, 이는 DBR 17.3 이상에서 새로 생성되는 쿼리의 기본값으로 이미 적용되어 있다.
- {"duration_api_ms":2200,"stop_reason":"end_turn","session_id":"c3058788-edcd-4195-aac1-7ea1a117b8c2","total_cost_usd":0.003154,"usage":{"input_tokens":2,"cache_creation_input_tokens":0,"cache_read_input_tokens":9015,"output_tokens":20,"output_tokens_details":{"thinking_tokens":0},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":2,"output_tokens":20,"cache_read_input_tokens":9015,"cache_creation_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"modelUsage":{"claude-haiku-4-5-20251001":{"inputTokens":1062,"outputTokens":17,"cacheReadInputTokens":0,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.001147,"contextWindow":200000,"maxOutputTokens":32000,"thinkingTokens":0,"canonicalModel":"claude-haiku-4-5","provider":"firstParty","costBasis":"list"},"claude-sonnet-5":{"inputTokens":2,"outputTokens":20,"cacheReadInputTokens":9015,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.002007,"contextWindow":1000000,"maxOutputTokens":64000,"thinkingTokens":0,"canonicalModel":"claude-sonnet-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"파티션 수는 어떻게 변경하나요?","ttft_ms":2378,"type":"result","duration_ms":2455,"uuid":"050a292d-cd99-44d3-92e4-08379c9e3b4a","ttft_stream_ms":1890,"time_to_request_ms":1231,"queued_turn_count":0} Client.listTools() called but server does not advertise tools capability - returning empty list
- Databricks에 따르면, 쿼리를 중단한 뒤 spark.sql.streaming.stateStore.partitions 설정을 새 값으로 지정하고 동일한 체크포인트를 사용해 쿼리를 재시작하면 된다. 그러면 쿼리가 처리를 재개하기 전에 상태를 새로운 파티션 수에 맞게 재분배한다.
- 리파티션 작업을 어떻게 모니터링할 수 있나요?
- {"duration_api_ms":2473,"stop_reason":"end_turn","session_id":"3a7e8b98-7565-4c31-b4b2-7e3406e5fea1","total_cost_usd":0.003782,"usage":{"input_tokens":2,"cache_creation_input_tokens":0,"cache_read_input_tokens":9055,"output_tokens":79,"output_tokens_details":{"thinking_tokens":0},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":0},"inference_geo":"not_available","iterations":[{"input_tokens":2,"output_tokens":79,"cache_read_input_tokens":9055,"cache_creation_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"modelUsage":{"claude-haiku-4-5-20251001":{"inputTokens":1087,"outputTokens":18,"cacheReadInputTokens":0,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.001177,"contextWindow":200000,"maxOutputTokens":32000,"thinkingTokens":0,"canonicalModel":"claude-haiku-4-5","provider":"firstParty","costBasis":"list"},"claude-sonnet-5":{"inputTokens":2,"outputTokens":79,"cacheReadInputTokens":9055,"cacheCreationInputTokens":0,"webSearchRequests":0,"costUSD":0.002605,"contextWindow":1000000,"maxOutputTokens":64000,"thinkingTokens":0,"canonicalModel":"claude-sonnet-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"Databricks에 따르면 StreamingQueryProgress 이벤트는 controlBatch.REPARTITION 필드의 durationMs 메트릭을 통해 해당 작업의 소요 시간을 보고한다.","ttft_ms":2175,"type":"result","duration_ms":2250,"uuid":"6df58e80-65a4-41ea-b0bd-b0406298cfad","ttft_stream_ms":1778,"time_to_request_ms":721,"queued_turn_count":0} Client.listTools() called but server does not advertise tools capability - returning empty list