Skip to content

Split View: InfluxDB 3 Core의 72시간 제한은 사실 432개 파일 제한이다 — Parquet 재작성이 남긴 청구서

|

InfluxDB 3 Core의 72시간 제한은 사실 432개 파일 제한이다 — Parquet 재작성이 남긴 청구서

들어가며 — 72시간이라는 숫자의 정체

InfluxDB 3를 검토해 본 사람이라면 "오픈소스 Core는 최근 72시간까지만 쿼리할 수 있다"는 문장을 어딘가에서 봤을 겁니다. 커뮤니티 포럼에도, 비교 글에도 반복해서 나옵니다.

그런데 소스를 읽어 보면 72시간을 세는 코드는 없습니다. 시간 범위를 검사하는 로직 자체가 존재하지 않습니다. 실제로 존재하는 것은 이것 하나입니다 — 쿼리 하나가 건드릴 수 있는 Parquet 파일의 개수 상한. 기본값은 432개입니다.

이 구분은 말장난이 아닙니다. 제한의 단위가 시간이 아니라 파일이라는 사실이 이 제한의 성격, 이 제한이 언제 더 빨리 걸리는지, 그리고 애초에 왜 존재하는지를 전부 설명합니다. 그리고 그 답은 곧장 하나의 아키텍처 결정으로 이어집니다 — 저장 계층을 Parquet에 걸었다는 결정 말입니다.

이 글은 그 청구서를 읽습니다. 시계열 DB 진영 전체의 지형도는 시계열 데이터베이스 심층 비교 편에서 다뤘으니, 여기서는 InfluxDB 3 한 곳만 깊게 파겠습니다.

432는 어디서 왔나

InfluxDB 3 Core는 influxdata/influxdb 저장소에 MIT 또는 Apache-2.0으로 공개돼 있습니다. 그래서 이건 추측할 필요가 없는 종류의 질문입니다. 그냥 읽으면 됩니다.

먼저 플래그 정의입니다. influxdb3/src/commands/serve.rs의 주석은 이렇게 말합니다.

/// Set the limit for number of parquet files allowed in a query. Defaults
/// to 432 which is about 3 days worth of files using default settings.
/// This number can be increased to allow more files to be queried, but
/// query performance will likely suffer, RAM usage will spike, and the
/// process might be OOM killed as a result. It would be better to specify
/// smaller time ranges if possible in a query.
#[clap(long = "query-file-limit", env = "INFLUXDB3_QUERY_FILE_LIMIT", action)]
pub query_file_limit: Option<usize>,

플래그는 옵션 타입이고, 값을 안 주면 influxdb3_write/src/write_buffer/mod.rs에서 기본값이 박힙니다. 릴리스 태그 v3.10.0 기준 437번째 줄입니다.

query_file_limit: query_file_limit.unwrap_or(432),

그러면 432는 왜 432일까요. InfluxDB 3는 들어오는 데이터를 타임스탬프 기준으로 일정 길이의 시간 블록에 담아 Parquet 파일로 떨굽니다. 이 블록을 gen1이라고 부르고, 길이는 --gen1-duration으로 정합니다.

/// Duration that the Parquet files get arranged into. The data timestamps will land each
/// row into a file of this duration. 1m, 5m, and 10m are supported. These are known as
/// "generation 1" files. The compactor in Pro can compact these into larger and longer
/// generations.
#[clap(long = "gen1-duration", env = "INFLUXDB3_GEN1_DURATION", default_value = "10m", action)]
pub gen1_duration: Gen1Duration,

기본값 10분이고, 허용값은 1분·5분·10분 셋뿐입니다. 코드에서도 Duration::from_secs(600)이 기본이고, 파서는 60·300·600초만 통과시킵니다.

이제 산수가 맞아떨어집니다.

gen1 블록 = 10분
시간당 파일 = 60 / 10 = 6개
하루 파일  = 6 x 24 = 144개
432 / 144 = 3일 = 72시간

72시간은 입력값이 아니라 파생값입니다. 432라는 파일 개수 상한에 10분이라는 블록 길이를 곱해서 나온 결과일 뿐입니다. 공식 문서도 같은 이야기를 합니다 — 기본값 432와 기본 gen1-duration 10분을 쓰면 쿼리가 최대 72시간 분량의 데이터에 접근할 수 있다고요.

--gen1-duration을 1분으로 낮추면 어떻게 될까요. 432개 파일이 곧 432분, 약 7.2시간이 됩니다. 같은 432인데 조회 가능 범위가 10분의 1로 줄어듭니다. 시간 제한이 아니라 파일 제한이라는 말의 실질적 의미가 이겁니다.

하드 제한에서 소프트 제한으로

원래는 진짜로 시간 제한이었습니다. 초기 InfluxDB 3 Core에는 쿼리와 쓰기 양쪽에 72시간 하드 제한이 걸려 있었고, 이걸 걷어낸 것이 PR #25890입니다. GitHub API로 확인한 메타데이터 기준 2025년 1월 21일에 열려 1월 23일에 병합됐습니다.

PR 본문의 설명이 이 글의 요약이나 다름없습니다.

  • Core의 72시간 쿼리·쓰기 제한을 제거한다
  • 대신 쿼리를 기본 Parquet 파일 개수로 제한한다. 432를 골랐는데, gen1 타임블록 기본 설정 기준 약 72시간에 해당하기 때문이다
  • 이 파일 상한은 올릴 수 있지만, 도움말과 에러 메시지에서 쿼리 성능이 나빠질 것임을 알린다
  • 이 에러를 만나면 가능하면 더 좁은 시간 범위를 쓰라고 경고한다

그리고 마지막 문장이 핵심입니다 — 하드 제한을 없애고 사용자가 성능 손해를 감수하고 선택할 수 있는 소프트 제한을 만들되, 그 손해를 감수할 수 없다면 컴팩터가 내장된 Enterprise로 올라가는 것을 권한다.

이 문장은 2025년 1월에 쓰였고, 2026년 7월 현재까지 그대로 유효합니다.

72시간은 최선의 경우다

여기서부터가 실무에서 사람들이 놀라는 지점입니다. 문서는 432개와 10분을 곱해 72시간이 나온다고 말한 뒤, 곧바로 단서를 답니다 — 다만 주어진 10분 블록의 데이터가 전부 같은 시기에 인제스트됐는지에 따라 그보다 짧을 수 있다고요.

왜 그런지는 파일 경로를 만드는 코드를 보면 명확합니다. influxdb3_write/src/paths.rs입니다.

{host_prefix}/dbs/{db_id}/{table_id}/{YYYY-MM-DD}/{HH-MM}/{wal_seq}.parquet

디렉터리까지가 gen1 블록(날짜 + 시·분)이고, 파일 이름은 WAL 시퀀스 번호입니다. 즉 하나의 10분 블록 안에 서로 다른 WAL 시퀀스로 만들어진 파일이 여러 개 공존할 수 있습니다. 같은 10분에 대해 스냅샷이 두 번 일어나면 파일이 두 개 생기고, 그 블록은 이제 파일 예산 432개 중 2개를 먹습니다.

기본 설정에서 이 일이 잘 안 일어나도록 맞춰져 있긴 합니다. --wal-flush-interval 기본이 1초, --wal-snapshot-size 기본이 600이고, 플래그 설명대로 이 둘을 곱한 값이 스냅샷 주기를 정합니다. 600초 = 10분이니 스냅샷 주기가 gen1 블록 길이와 정확히 맞고, 따라서 블록당 파일 하나가 됩니다.

문제는 데이터가 항상 얌전히 도착하지 않는다는 겁니다. 과거 시점으로 백필을 하거나, 지연 도착 데이터가 이미 스냅샷된 블록으로 들어오면 그 블록에 파일이 추가로 생깁니다. 그러면 432개 예산이 72시간보다 빨리 소진됩니다. 백필이 잦은 워크로드일수록 "72시간"이라는 숫자는 점점 거짓말이 됩니다.

정리하면 72시간은 보장이 아니라 상한선이고, 그것도 데이터가 실시간으로만 들어올 때의 상한선입니다.

제한에 걸리면 무슨 일이 생기나

강제 지점은 write_buffer/mod.rs에서 파일 목록을 세는 곳입니다. 로직 자체는 세 줄 요약이 가능합니다 — 해당 테이블에서 필터를 통과한 Parquet 파일을 모으고, 개수가 상한을 넘으면 DataFusion 에러를 던지고 끝냅니다.

여기서 세 가지를 짚어야 합니다.

첫째, 제한은 테이블 단위입니다. 파일 목록을 가져올 때 데이터베이스 ID와 테이블 ID로 필터링하므로, 432는 "쿼리 전체"가 아니라 "쿼리가 건드리는 테이블 하나"의 예산입니다.

둘째, 이건 스캔 후 실패가 아니라 계획 단계의 거절입니다. 파일을 열어 보고 느려지는 게 아니라, 파일 개수를 세어 보고 아예 실행을 거부합니다. 부분 결과도 없습니다.

셋째, 에러 메시지에 사소한 버그가 있습니다. 메시지의 자리표시자에 넘기는 포맷 인자가 실제로 스캔하려던 파일 수가 아니라 상한값 자체입니다.

format!(
    "Query would scan {} Parquet files, exceeding the file limit. \
     InfluxDB 3 Core caps file access to prevent performance degradation \
     and memory issues. Use a narrower time range, or increase the limit \
     with --query-file-limit ...",
    self.query_file_limit
)

인자가 하나뿐이고 그 값이 상한입니다. 그래서 5,000개짜리 쿼리를 던져도 메시지는 언제나 "Query would scan 432 Parquet files"라고 나옵니다. 내가 얼마나 초과했는지는 이 에러로 알 수 없습니다. 시간 범위를 얼마나 줄여야 하는지 감을 잡으려던 사람에게는 꽤 불친절한 동작입니다.

메시지 본문은 상한을 올리라고 권하지 않습니다. 더 좁은 시간 범위를 쓰거나, 컴팩터가 들어 있는 Enterprise로 올라가라고 안내합니다. 비상업·가정용은 무료이고 상업 평가용 무료 체험이 있다는 문구까지 에러 메시지 안에 들어 있습니다.

올리면 되지 않나 — 오브젝트 스토리지 청구서

--query-file-limit을 4320으로 올려서 30일을 조회하면 안 되나요? 됩니다. 다만 문서가 나열한 부작용이 있습니다.

  • Parquet 파일을 더 많이 읽는 쿼리의 성능 저하
  • 메모리 사용량 증가
  • OOM으로 influxdb3 프로세스가 죽을 가능성
  • 오브젝트 스토리지를 쓴다면 데이터 접근을 위한 다수의 GET 요청 — 문서 표현으로 파일당 최대 2회

마지막 항목이 조용한 킬러입니다. 기본값 432개만 해도 쿼리 한 번에 최대 864회의 GET입니다. 이걸 4320개로 올리면 대시보드가 자동 새로고침할 때마다 쿼리 하나당 최대 8,640회의 GET이 나갑니다. S3 GET 단가는 요청 1,000건 단위로 매겨지므로, 이건 성능 문제이기 전에 청구서 문제입니다.

그리고 이건 Parquet 설계의 직접적 귀결입니다. Parquet 파일 하나를 읽으려면 푸터의 메타데이터를 먼저 읽고, 그다음 필요한 로우 그룹을 읽어야 합니다. 파일이 작고 많을수록 실제 데이터 대비 메타데이터 왕복 비율이 나빠집니다. 10분마다 파일 하나씩 떨어지는 구조는 정확히 그 최악의 형태입니다.

문서가 내리는 권고는 솔직합니다 — 기본 설정을 유지하고 더 짧은 시간 범위를 조회하라. 한 시간 이상을 건드리는 쿼리에서 더 긴 범위나 더 빠른 성능이 필요하면 Enterprise를 쓰라.

컴팩터가 답이고, 컴팩터는 유료다

작은 불변 파일이 쌓이는 문제의 정답은 컴팩션입니다. 작은 파일들을 더 크고 긴 시간 범위의 파일로 다시 써서, 파일 개수를 줄이고 시리즈 기준으로 정렬하고 인덱스를 붙이는 것.

InfluxDB 3에 그 컴팩터가 있습니다. Enterprise에만 있습니다.

이건 문서 구조에서도 그대로 드러납니다. 컴팩터 전용 노드를 띄우는 --mode=compact 설정 예시는 문서 소스에서 Enterprise 전용 블록 안에 들어 있고, mode 옵션 자체가 Enterprise 전용 항목으로 분류돼 있습니다. Core에는 컴팩터 노드라는 개념이 없습니다.

Core 문서의 첫 문장이 이 사실을 에둘러 말합니다 — InfluxDB 3 Core는 실시간 및 최근 데이터에 맞춰 설계·최적화된 오픈소스 시계열 데이터베이스라고요. 이건 마케팅 문구이기 이전에 아키텍처 설명입니다. 컴팩터가 없으니 최근 데이터만 볼 수 있고, 그래서 최근 데이터용이라고 포지셔닝한 겁니다.

같은 구조가 다른 곳에도 반복됩니다. Core의 README가 자랑하는 성능 수치는 last-value 쿼리 10ms 미만, distinct 메타데이터 30ms 미만인데, 이건 일반 쿼리 성능이 아니라 last-value 캐시와 distinct value 캐시의 성능입니다. 두 캐시는 메모리에 얹은 별도 구조물입니다. Parquet 레이아웃 위에서 "이 시리즈의 마지막 값" 같은 포인트 조회가 빠를 수 없기 때문에 따로 만든 것이고, 캐시가 필요했다는 사실 자체가 저장 계층이 그 질문에 약하다는 증거입니다.

2026년, 삭제도 컴팩터의 일이 되다

2026년 6월 17일 릴리스된 v3.10.0에서 Enterprise에 행 단위 삭제가 들어왔습니다. 그런데 구현 방식이 이 글의 주제를 그대로 반복합니다.

문서에 따르면 삭제는 비동기입니다. influxdb3 delete rows를 실행하면 삭제 요청이 기록돼 오브젝트 스토리지에 저장되고, 컴팩터가 해당 데이터를 다시 쓸 때 요청이 적용됩니다. 기본적으로 요청 후 최대 24시간까지 삭제가 반영되지 않으며, 이 지연은 --pt-row-delete-min-age 플래그로 조정합니다. 문서는 못을 박습니다 — 삭제가 컴팩션 중에 적용되므로 컴팩터가 요청을 처리하기 전까지 행은 계속 조회되며, 명령이 반환됐다고 행이 즉시 사라질 것으로 기대하지 말라고요.

술어는 태그 동등 비교만 지원합니다. 필드 값으로는 지울 수 없습니다.

그리고 v3.10.0 문서의 알려진 이슈 목록에 이런 항목이 있습니다 — 행 삭제가 "완료"로 보고된 뒤에도 컴팩션되지 않은 인제스트 꼬리 부분의 행이 살아남아 쿼리에 계속 보일 수 있다. 회피책은 해당 데이터가 컴팩션된 뒤 삭제 요청을 다시 넣고 행 수를 확인하는 것.

이건 우연한 버그가 아니라 설계의 논리적 귀결입니다. 삭제가 파일 재작성으로 구현되면, 아직 재작성되지 않은 영역에서는 삭제가 존재하지 않습니다. Parquet 파일은 불변이니까요.

Core 쪽은 어떨까요. 공식 문서 저장소에서 Core의 관리 문서 목록에는 delete-data.md가 아예 없습니다. Enterprise에만 있습니다. 컴팩터가 없으면 행을 지울 방법도 없습니다. GDPR 삭제 요청이 오는 종류의 데이터를 오픈소스 Core에 넣고 있다면 지금 확인해 보시길 권합니다.

2026년의 진짜 뉴스 — Parquet 위에 새 엔진

여기까지가 배경이고, 이제 올해의 소식입니다.

2026년 4월 2일 릴리스된 v3.9.0에서 InfluxData는 성능 업그레이드 프리뷰(베타) 를 열었습니다. --use-pacha-tree 플래그로 켜고, 릴리스 노트의 표현으로는 새 컬럼 파일 포맷(.pt 파일), 하이브리드 쿼리 모드를 동반한 자동 Parquet 마이그레이션, 넓은 테이블 I/O를 위한 컬럼 패밀리, 그리고 경계가 있는 컴팩션을 포함합니다.

정리하면 InfluxDB가 Parquet에서 부분적으로 나오고 있습니다. InfluxDB 3의 첫 정식 릴리스인 v3.0.0이 2025년 4월 16일이었으니, GA로부터 약 1년 만입니다.

왜인지는 추측할 필요가 없습니다. 벤더가 직접 써 놨습니다. 프리뷰 문서의 "Why these upgrades" 절은 이렇게 시작합니다 — 기존 InfluxDB 3 저장 계층은 Apache Parquet을 사용하며 분석 워크로드에 최적화돼 있다. 그리고 고카디널리티·와이드 스키마·쿼리 집약적 워크로드를 돌리는 고객들은 더 나은 단일 시리즈 쿼리 성능, 더 예측 가능한 리소스 사용량, 그리고 InfluxDB v1과 v2를 인기 있게 만들었던 스키마 유연성을 필요로 한다.

마지막 구절을 다시 읽어 볼 만합니다. v1과 v2를 인기 있게 만들었던 것을 되찾겠다는 문장입니다. v1·v2의 저장 엔진이 바로 시계열 전용으로 만든 TSM이었고, 3.0에서 버린 것이 그 TSM이었습니다.

.pt 포맷의 설명을 보면 그 회귀가 더 분명해집니다. 문서에 따르면 파일 내 데이터는 컬럼 패밀리 키·시리즈 키·타임스탬프 순으로 정렬되고, 데이터 특성에 맞춰 타입별 압축 알고리즘을 씁니다 — 타임스탬프에는 delta-delta RLE, 부동소수점에는 Gorilla 인코딩, 저카디널리티 문자열에는 딕셔너리 인코딩 등.

여기서 Gorilla가 눈에 띕니다. Parquet 포맷 명세의 인코딩 목록을 직접 확인해 보면 PLAIN, 딕셔너리(PLAIN_DICTIONARY / RLE_DICTIONARY), RLE, BIT_PACKED(폐기됨), DELTA_BINARY_PACKED, DELTA_LENGTH_BYTE_ARRAY, DELTA_BYTE_ARRAY, BYTE_STREAM_SPLIT가 전부입니다. 부동소수점 XOR 계열 인코딩, 즉 Gorilla는 명세에 없습니다. 시계열 float 압축의 표준 기법을 쓰려면 Parquet 바깥으로 나가는 수밖에 없었던 겁니다.

압축률에 대해 문서는 일반적으로 5~20배를 달성한다고 적었습니다. 다만 이건 벤더 자체 주장이고, 어떤 데이터셋·어떤 스키마·어떤 비교 대상 기준인지는 문서에 없습니다. 비교 대상이 Parquet인지 원본 line protocol인지조차 명시돼 있지 않으므로, 이 숫자로 용량 계획을 세우면 안 됩니다. 마찬가지로 "고선택도 시계열 쿼리에서 한 자릿수 밀리초 응답"이라는 문구도 측정 조건이 공개돼 있지 않습니다. 목표치로 읽는 편이 안전합니다.

컬럼 패밀리는 line protocol 필드 이름에 :: 구분자를 써서 지정합니다. 첫 :: 앞이 패밀리 이름이고, 지정하지 않은 필드는 자동 생성 패밀리에 100개씩 묶입니다. 넓은 테이블에서 특정 필드만 조회할 때 나머지 패밀리 블록을 아예 읽지 않는 구조입니다.

컴팩션도 바뀝니다. 문서에 따르면 컴팩트된 데이터를 24시간 UTC 윈도로 조직하고 Gen0 파일을 L1부터 L4까지 진행시키며, 바이트 기준 메모리 예산(기본값 시스템 RAM의 절반) 안에서 백그라운드로 계속 돕니다.

프리뷰의 값 — 정직하게

여기서 흥분하기 전에 문서가 붙여 놓은 경고들을 그대로 옮기겠습니다.

베타이고, 프로덕션 금지입니다. 문서의 경고 박스는 이 프리뷰가 Enterprise 체험·상용 사용자에게 베타로 제공되며, 변경이 있을 수 있고 프로덕션 워크로드에 사용해서는 안 된다고 명시합니다. 스테이징·테스트 환경 전용이라고 못 박습니다.

v3.10.0 알려진 이슈에 데이터 손실이 있습니다. --use-pacha-tree를 켠 상태에서 샤드를 2개 이상(--pt-shard-count) 돌리면 데이터 손실과 부트스트랩 데드락이 발생할 수 있습니다. 회피책은 샤드 수를 1로 유지하는 것. 즉 현재 이 프리뷰는 사실상 단일 샤드로만 쓸 수 있습니다.

다운그레이드는 되돌리기가 아닙니다. influxdb3 downgrade-to-parquet은 카탈로그를 갱신하고 모든 .pt 파일을 오브젝트 스토리지에서 지웁니다. 문서 표현으로 업그레이드 이후에 쓰인 데이터까지 포함해서 지우며, 업그레이드 이전에 존재하던 원본 Parquet 파일만 보존됩니다. 프리뷰를 켜고 한 달 데이터를 쌓은 뒤 마음을 바꾸면 그 한 달은 사라집니다.

3.10 업그레이드 자체가 편도입니다. 3.10을 처음 시작하면 온디스크 카탈로그가 v2에서 v3로 자동 마이그레이션되고, 그 뒤로는 3.9.x 이하 바이너리가 같은 클러스터 데이터를 읽지 못해 시작에 실패합니다. 릴리스 노트는 카탈로그 디렉터리와 카탈로그 체크포인트 객체를 미리 백업하라고, 그 객체를 복원하는 것이 3.9.x로 롤백하는 유일한 방법이라고 적었습니다. --use-pacha-tree를 켰다면 .pt 포맷으로 쓰인 데이터도 3.9.x가 읽지 못합니다.

그리고 이 전부가 Enterprise 전용입니다. Core는 .pt도, 컬럼 패밀리도, 경계 있는 컴팩션도 받지 못합니다. Core는 계속 432개 파일 예산 안에서 삽니다.

Core와 Enterprise의 온도 차는 릴리스 노트를 연달아 읽으면 노골적으로 보입니다. 2026년 6월 25일 v3.9.6, 6월 30일 v3.10.2와 v3.9.7 — 이 세 릴리스의 Core 항목은 전부 같은 문장입니다. 유지보수 릴리스이며 빌드와 의존성 업데이트만 포함하고 사용자가 체감할 변경은 없다. 같은 날짜의 Enterprise 항목에는 컴팩션 중복 제거 수정과 프로세싱 엔진 트리거 취소 수정이 들어 있습니다.

그래서, 언제 쓰고 언제 쓰지 말아야 하나

Core로 충분한 경우. Core는 나쁜 제품이 아닙니다. 자기가 하겠다고 말한 일을 합니다.

  • 조회 범위가 최근 몇 시간~며칠인 실시간 대시보드와 알림
  • 엣지·IoT 수집 지점에서 버퍼링 후 상류로 넘기는 용도
  • 최신 값 조회가 중심인 워크로드 — last-value 캐시가 이걸 위해 있습니다
  • 데이터가 실시간으로만 들어오고 백필이 없는 파이프라인

Core로는 안 되는 경우. 아래에 하나라도 해당하면 오픈소스 Core는 답이 아닙니다. 튜닝으로 넘길 수 있는 종류의 벽이 아닙니다.

  • 며칠을 넘는 과거 구간을 정기적으로 조회해야 한다 — 파일 상한을 올리면 GET 요청과 메모리가 같이 올라갑니다
  • 백필이나 지연 도착 데이터가 잦다 — 같은 gen1 블록에 파일이 겹쳐 쌓이고 72시간은 더 빨리 무너집니다
  • 행을 지워야 한다 — Core에는 방법이 없습니다
  • --gen1-duration을 1분으로 줄여야 하는 사정이 있다 — 조회 가능 범위가 7.2시간대로 내려갑니다

Enterprise 프리뷰를 켜 볼 만한 경우. 문서가 권하는 대상은 고카디널리티·와이드 테이블, 시간 범위를 넘나드는 잦은 백필, 낮은 지연이 필요한 쿼리 집약적 접근, 동적으로 컬럼이 생기는 희소 스키마, 그리고 메모리·CPU 사용량의 상한이 중요한 환경입니다. 다만 스테이징에서, 단일 샤드로, 지워도 되는 데이터로 시작하십시오.

대안을 봐야 하는 경우. 오픈소스로 장기 보관과 삭제와 과거 조회를 전부 해야 한다면, 이 선택지는 애초에 InfluxDB 3 Core가 아닐 수 있습니다. Postgres 확장으로 가든 컬럼 스토어로 가든, 비교의 출발점은 시계열 데이터베이스 심층 비교 편에 정리해 뒀습니다.

마치며

정리하면 이렇습니다. "InfluxDB 3 Core는 72시간 제한이 있다"는 문장은 틀린 요약입니다. 정확히는 쿼리 하나가 테이블 하나에서 스캔할 수 있는 Parquet 파일이 기본 432개로 제한되고, 기본 gen1 블록 10분을 곱하면 72시간이 나오며, 그 72시간조차 데이터가 실시간으로만 도착할 때의 최선값입니다.

이 제한은 자의적인 기능 제한이 아닙니다. Parquet 위에 시계열 DB를 지으면 필연적으로 작고 불변인 파일이 대량으로 생기고, 그걸 정리하는 유일한 방법이 컴팩션이며, InfluxData는 그 컴팩터를 상용 경계선으로 삼았습니다. 오픈소스 Core는 그 결정의 모든 결과를 그대로 물려받습니다 — 파일 상한, 파일당 최대 2회의 GET, 그리고 삭제 기능의 부재.

그리고 2026년, 이 아키텍처의 청구서가 벤더 자신에게도 도착했습니다. Parquet이 분석 워크로드에 최적화돼 있다는 것, 그래서 단일 시리즈 쿼리와 와이드 스키마에는 부족하다는 것 — 이건 제가 하는 말이 아니라 InfluxData의 프리뷰 문서에 적힌 말입니다. 그 결과가 .pt이고, 거기에는 Parquet 명세에 없는 Gorilla 인코딩이 들어갑니다. v1과 v2의 TSM이 갖고 있던 바로 그 기법이요.

교훈이 있다면 범용 포맷을 고르는 일의 성격에 대한 것 같습니다. Arrow와 Parquet과 DataFusion을 고른 덕분에 InfluxDB 3는 SQL과 Flight SQL과 생태계 도구를 거의 공짜로 얻었습니다. 그건 진짜 이득이었습니다. 다만 범용 포맷은 범용적인 것에 최적화돼 있고, 당신의 워크로드가 그 평균에서 멀어질수록 차액을 어딘가에서 지불하게 됩니다. InfluxDB 3의 경우 그 차액은 컴팩터라는 이름의 유료 컴포넌트와, GA 1년 만에 자체 파일 포맷을 다시 만드는 일로 청구됐습니다.

기술 선택을 검토할 때 릴리스 노트의 기능 목록보다 이런 걸 먼저 보시길 권합니다 — 이 시스템의 근본 제약이 무엇이고, 그 제약을 푸는 컴포넌트가 어느 라이선스 쪽에 있는가.

참고 자료

InfluxDB 3 Core's 72-Hour Limit Is Actually a 432-File Limit — The Bill Left by Rewriting Parquet

Introduction — What the 72-Hour Number Actually Is

Anyone who has evaluated InfluxDB 3 has probably run across the line "the open-source Core edition can only query the last 72 hours" somewhere. It shows up repeatedly in community forums and in comparison posts.

But read the source, and there is no code that counts 72 hours. The logic to check a time range simply does not exist. What does exist is exactly one thing — a cap on the number of Parquet files a single query is allowed to touch. The default is 432.

This distinction is not wordplay. The fact that the limit's unit is files, not time, explains the limit's character, when it triggers earlier, and why it exists in the first place. And that answer leads straight to one architectural decision — the decision to build the storage layer on Parquet.

This post reads that bill. The landscape across the whole time-series database camp was covered in Time Series Databases 2026 Deep Dive; here I dig into InfluxDB 3 alone, in depth.

Where 432 Comes From

InfluxDB 3 Core is published under MIT or Apache-2.0 in the influxdata/influxdb repository. So this isn't a question you need to guess at — you can just read it.

First, the flag definition. The comment in influxdb3/src/commands/serve.rs reads:

/// Set the limit for number of parquet files allowed in a query. Defaults
/// to 432 which is about 3 days worth of files using default settings.
/// This number can be increased to allow more files to be queried, but
/// query performance will likely suffer, RAM usage will spike, and the
/// process might be OOM killed as a result. It would be better to specify
/// smaller time ranges if possible in a query.
#[clap(long = "query-file-limit", env = "INFLUXDB3_QUERY_FILE_LIMIT", action)]
pub query_file_limit: Option<usize>,

The flag is an Option type, and when no value is given, the default is hard-coded in influxdb3_write/src/write_buffer/mod.rs. In release tag v3.10.0, that's line 437.

query_file_limit: query_file_limit.unwrap_or(432),

So why 432? InfluxDB 3 buckets incoming data by timestamp into fixed-length time blocks and flushes each as a Parquet file. This block is called gen1, and its length is set by --gen1-duration.

/// Duration that the Parquet files get arranged into. The data timestamps will land each
/// row into a file of this duration. 1m, 5m, and 10m are supported. These are known as
/// "generation 1" files. The compactor in Pro can compact these into larger and longer
/// generations.
#[clap(long = "gen1-duration", env = "INFLUXDB3_GEN1_DURATION", default_value = "10m", action)]
pub gen1_duration: Gen1Duration,

The default is 10 minutes, and only three values are allowed: 1, 5, or 10 minutes. In the code, too, Duration::from_secs(600) is the default, and the parser only accepts 60, 300, or 600 seconds.

Now the arithmetic lines up.

gen1 block = 10 minutes
files per hour = 60 / 10 = 6
files per day  = 6 x 24 = 144
432 / 144 = 3 days = 72 hours

In other words, 72 hours is not an input — it's a derived value. It's simply the result of multiplying the 432-file cap by the 10-minute block length. The official docs say the same thing — with the default of 432 and the default gen1-duration of 10 minutes, a query can reach up to 72 hours' worth of data.

What happens if you lower --gen1-duration to 1 minute? 432 files becomes 432 minutes, about 7.2 hours. Same 432, but the queryable window shrinks to a tenth. That's the practical meaning of "it's a file limit, not a time limit."

From a Hard Limit to a Soft One

It really was a time limit at first. Early InfluxDB 3 Core had a hard 72-hour limit on both queries and writes, and the change that removed it is PR #25890. By the metadata from the GitHub API, it was opened on January 21, 2025 and merged on January 23.

The PR description is practically a summary of this post.

  • Remove Core's 72-hour query/write limit
  • Instead, cap queries by the default number of Parquet files. 432 was chosen because it corresponds to roughly 72 hours under the default gen1 time-block settings
  • This file cap can be raised, but the help text and error messages warn that query performance will degrade
  • If you hit this error, use a narrower time range where possible

And the last line is the crux — remove the hard limit and replace it with a soft limit users can opt into at the cost of performance, but if they can't accept that cost, they're pointed toward Enterprise, which ships with a built-in compactor.

That was written in January 2025, and as of July 2026 it still holds.

72 Hours Is the Best Case

This is where practitioners tend to be surprised. The docs state that multiplying 432 by 10 minutes yields 72 hours, then immediately attach a caveat — it can be shorter, depending on whether all the data in a given 10-minute block was ingested at the same time.

Why becomes clear from the code that builds file paths, in influxdb3_write/src/paths.rs.

{host_prefix}/dbs/{db_id}/{table_id}/{YYYY-MM-DD}/{HH-MM}/{wal_seq}.parquet

Everything up to the directory is the gen1 block (date + hour-minute), and the file name is the WAL sequence number. That means multiple files created under different WAL sequences can coexist within a single 10-minute block. If a snapshot happens twice for the same 10 minutes, you get two files, and that block now eats 2 of the 432-file budget.

Under default settings, this is tuned not to happen often. The default --wal-flush-interval is 1 second, the default --wal-snapshot-size is 600, and per the flag description, multiplying the two sets the snapshot cadence. 600 seconds is 10 minutes, so the snapshot cadence lines up exactly with the gen1 block length, yielding one file per block.

The problem is data doesn't always arrive so obediently. Backfilling to a past point in time, or late-arriving data landing in a block that's already been snapshotted, adds an extra file to that block. That burns through the 432-file budget faster than 72 hours. The more backfill-heavy the workload, the more "72 hours" becomes a lie.

To sum up: 72 hours is not a guarantee, it's a ceiling — and even then, only the ceiling for data that arrives strictly in real time.

What Happens When You Hit the Limit

The enforcement point is where write_buffer/mod.rs counts the file list. The logic itself fits in three lines — gather the Parquet files that pass the filter for that table, and if the count exceeds the cap, throw a DataFusion error and stop.

Three things are worth flagging here.

First, the limit is per table. Because the file list is filtered by database ID and table ID, 432 is not a budget for "the whole query" but for "the single table the query touches."

Second, this is a rejection at planning time, not a failure after scanning. It doesn't open the files and then slow down — it counts the files and refuses to run at all. There are no partial results.

Third, there's a minor bug in the error message. The value passed into the message's format placeholder is not actually the number of files the query would have scanned — it's the cap itself.

format!(
    "Query would scan {} Parquet files, exceeding the file limit. \
     InfluxDB 3 Core caps file access to prevent performance degradation \
     and memory issues. Use a narrower time range, or increase the limit \
     with --query-file-limit ...",
    self.query_file_limit
)

There's only one argument, and its value is the cap. So even a query that would scan 5,000 files always prints "Query would scan 432 Parquet files." The error doesn't tell you how much you exceeded it by. For anyone trying to gauge how much to shrink their time range, that's a fairly unhelpful behavior.

The message body doesn't recommend raising the cap. It steers you toward a narrower time range, or toward Enterprise, which ships with the compactor. The error message even includes the note that non-commercial/home use is free and there's a free trial for commercial evaluation.

Why Not Just Raise It — the Object-Storage Bill

Could you just raise --query-file-limit to 4320 and query 30 days? You could. But the docs list side effects.

  • Degraded performance for queries that read more Parquet files
  • Increased memory use
  • A chance the influxdb3 process gets OOM-killed
  • If you're on object storage, a large number of GET requests to access the data — in the docs' own words, up to 2 per file

That last item is the quiet killer. Even at the default of 432, a single query can issue up to 864 GETs. Raise it to 4320, and every time a dashboard auto-refreshes, a single query can issue up to 8,640 GETs. S3 GET pricing is billed per 1,000 requests, so before this is a performance problem, it's a billing problem.

And this is a direct consequence of Parquet's design. Reading one Parquet file means reading the footer metadata first, then reading the row groups you need. The smaller and more numerous the files, the worse the ratio of metadata round-trips to actual data. A structure that drops one file every 10 minutes is exactly that worst case.

The docs' recommendation is candid — keep the default settings and query shorter time ranges. If a query touching more than an hour needs a longer range or faster performance, use Enterprise.

The Compactor Is the Answer, and the Compactor Is Paid

The correct fix for the problem of small immutable files piling up is compaction — rewriting small files into larger files spanning longer time ranges, reducing file count, sorting by series, and attaching indexes.

InfluxDB 3 has that compactor. Only in Enterprise.

This shows up directly in the docs' own structure. The example configuration for spinning up a dedicated compactor node with --mode=compact sits inside an Enterprise-only block in the docs source, and the mode option itself is classified as an Enterprise-only item. Core has no concept of a compactor node at all.

The Core docs' opening sentence says this obliquely — InfluxDB 3 Core is an open-source time-series database designed and optimized for real-time and recent data. That's an architectural description before it's marketing copy. Without a compactor, you can only see recent data, so it's positioned for recent data.

The same pattern repeats elsewhere. The performance numbers Core's README boasts about — sub-10ms last-value queries, sub-30ms distinct metadata — aren't general query performance; they're the performance of the last-value cache and the distinct-value cache. Both caches are separate structures bolted on in memory. They were built separately because a point lookup like "the last value for this series" can't be fast on top of a Parquet layout, and the very fact that a cache was needed is evidence that the storage layer is weak at that question.

2026: Deletion Becomes the Compactor's Job Too

v3.10.0, released June 17, 2026, brought row-level deletes to Enterprise. But the implementation repeats this post's theme exactly.

Per the docs, deletion is asynchronous. Running influxdb3 delete rows records a delete request and stores it in object storage, and the request is applied when the compactor next rewrites that data. By default, the delete may not take effect for up to 24 hours after the request, and that delay is tuned with the --pt-row-delete-min-age flag. The docs are explicit — because deletion is applied during compaction, rows remain queryable until the compactor processes the request, and you should not expect rows to vanish immediately just because the command returned.

The predicate only supports tag equality comparisons. You cannot delete by field value.

And v3.10.0's known-issues list for the docs includes this — after row deletion is reported "complete," rows in the not-yet-compacted ingest tail can survive and keep showing up in queries. The workaround is to re-issue the delete request after that data has been compacted and verify the row count.

This isn't an accidental bug — it's the logical consequence of the design. If deletion is implemented as file rewriting, then deletion doesn't exist in regions that haven't been rewritten yet. Parquet files are immutable.

What about Core? In the official docs repository, Core's admin docs list has no delete-data.md at all. It's Enterprise-only. No compactor means no way to delete rows. If you're putting data that draws GDPR deletion requests into open-source Core, it's worth checking on this now.

The Real News of 2026 — A New Engine on Top of Parquet

That's the background. Now for this year's news.

In v3.9.0, released April 2, 2026, InfluxData opened a performance upgrade preview (beta). Turned on with the --use-pacha-tree flag, it includes, in the release notes' own words, a new columnar file format (.pt files), automatic Parquet migration with a hybrid query mode, column families for wide-table I/O, and bounded compaction.

In short, InfluxDB is partly stepping off of Parquet. v3.0.0, InfluxDB 3's first GA release, shipped April 16, 2025 — so this is roughly a year after GA.

You don't have to guess why. The vendor wrote it down directly. The preview docs' "Why these upgrades" section opens like this — the existing InfluxDB 3 storage layer uses Apache Parquet and is optimized for analytical workloads. And customers running high-cardinality, wide-schema, query-intensive workloads need better single-series query performance, more predictable resource usage, and the schema flexibility that made InfluxDB v1 and v2 popular.

That last phrase is worth rereading. It's a sentence about reclaiming what made v1 and v2 popular. v1 and v2's storage engine was TSM, built specifically for time series — and TSM is exactly what 3.0 threw away.

The description of the .pt format makes that regression clearer still. Per the docs, data within a file is sorted by column-family key, then series key, then timestamp, and uses type-specific compression algorithms matched to the data's characteristics — delta-delta RLE for timestamps, Gorilla encoding for floats, dictionary encoding for low-cardinality strings, and so on.

Gorilla stands out here. Check the Parquet format spec's encoding list directly, and that's the complete set: PLAIN, dictionary (PLAIN_DICTIONARY / RLE_DICTIONARY), RLE, BIT_PACKED (deprecated), DELTA_BINARY_PACKED, DELTA_LENGTH_BYTE_ARRAY, DELTA_BYTE_ARRAY, and BYTE_STREAM_SPLIT. The XOR family of float encodings — Gorilla — is not in the spec. Using the standard technique for time-series float compression meant stepping outside Parquet.

On compression ratio, the docs state it typically achieves 5–20x. But this is a vendor's own claim, and the docs don't say against which dataset, which schema, or which baseline. It isn't even specified whether the comparison is against Parquet or raw line protocol, so this figure shouldn't be used for capacity planning. Likewise, the phrase "single-digit-millisecond response for high-selectivity time-series queries" doesn't disclose its measurement conditions. It's safer to read it as a target than a measurement.

Column families are specified using a :: separator in the line-protocol field name. Whatever comes before the first :: is the family name, and unspecified fields are grouped 100 at a time into auto-generated families. The structure means that when you query only specific fields in a wide table, you skip reading the other family blocks entirely.

Compaction changes too. Per the docs, compacted data is organized into 24-hour UTC windows, Gen0 files progress from L1 through L4, and it runs continuously in the background within a byte-based memory budget (default: half of system RAM).

What the Preview Is Worth — Honestly

Before getting excited, let me carry over the warnings the docs themselves attach.

It's beta, and production use is off the table. The docs' warning box states plainly that this preview is offered as beta to Enterprise trial and paid customers, is subject to change, and must not be used for production workloads. It says explicitly that it's for staging and test environments only.

v3.10.0's known issues include data loss. Running with --use-pacha-tree enabled and two or more shards (--pt-shard-count) can trigger data loss and a bootstrap deadlock. The workaround is to keep the shard count at 1. In other words, this preview is effectively usable only as a single shard right now.

Downgrading is not undoing. influxdb3 downgrade-to-parquet updates the catalog and deletes every .pt file from object storage. In the docs' words, this deletes even data written after the upgrade, and only the original Parquet files that existed before the upgrade are preserved. Turn the preview on, accumulate a month of data, then change your mind, and that month is gone.

The 3.10 upgrade itself is one-way. The first time you start 3.10, the on-disk catalog auto-migrates from v2 to v3, and after that, binaries at 3.9.x or earlier fail to start against the same cluster data. The release notes say to back up the catalog directory and the catalog checkpoint object beforehand, and that restoring that object is the only way to roll back to 3.9.x. If you enabled --use-pacha-tree, data written in the .pt format also can't be read by 3.9.x.

And all of this is Enterprise-only. Core gets none of .pt, column families, or bounded compaction. Core keeps living inside its 432-file budget.

The temperature difference between Core and Enterprise shows up bluntly if you read the release notes back to back. v3.9.6 on June 25, 2026, and v3.10.2 plus v3.9.7 on June 30 — all three releases' Core entries are the identical sentence: a maintenance release containing only build and dependency updates, with no user-facing changes. The Enterprise entries for the same dates include a compaction deduplication fix and a processing-engine trigger-cancellation fix.

So, When to Use It, and When Not To

When Core is enough. Core isn't a bad product. It does what it says it does.

  • Real-time dashboards and alerting where the query window is recent hours to a few days
  • Buffering at edge/IoT ingestion points before forwarding upstream
  • Workloads centered on latest-value lookups — the last-value cache exists for exactly this
  • Pipelines where data only arrives in real time, with no backfill

When Core won't do. If even one of the following applies, open-source Core isn't the answer. This isn't the kind of wall you tune your way past.

  • You need to regularly query stretches of history beyond a few days — raising the file cap raises GET requests and memory together
  • Backfill or late-arriving data is frequent — files pile up overlapping in the same gen1 block, and 72 hours collapses faster
  • You need to delete rows — Core has no way to do that
  • You have a reason to shrink --gen1-duration to 1 minute — your queryable window drops to around 7.2 hours

When it's worth trying the Enterprise preview. The docs recommend it for high-cardinality, wide tables; frequent backfill spanning time ranges; low-latency, query-intensive access; sparse schemas where columns appear dynamically; and environments where a memory/CPU usage ceiling matters. But start in staging, with a single shard, on data you can afford to lose.

When you should look elsewhere. If you need long-term retention, deletion, and historical queries all in open source, this choice might not be InfluxDB 3 Core to begin with. Whether you go to a Postgres extension or a column store, the starting point for comparison is laid out in Time Series Databases 2026 Deep Dive.

Closing

To sum up: "InfluxDB 3 Core has a 72-hour limit" is an inaccurate summary. More precisely, a single query is capped at scanning a default of 432 Parquet files in a single table; multiplying that by the default 10-minute gen1 block yields 72 hours; and even that 72 hours is the best case, valid only when data arrives strictly in real time.

This isn't an arbitrary feature restriction. Building a time-series DB on top of Parquet inevitably produces a large volume of small, immutable files, the only fix for that is compaction, and InfluxData drew the commercial line at that compactor. Open-source Core inherits every consequence of that decision as-is — the file cap, up to two GETs per file, and the absence of a delete feature.

And in 2026, the bill for this architecture arrived for the vendor too. That Parquet is optimized for analytical workloads, and therefore falls short for single-series queries and wide schemas — that's not my claim, it's written in InfluxData's own preview docs. The result is .pt, and it carries Gorilla encoding, which isn't in the Parquet spec — the very technique that v1 and v2's TSM had.

If there's a lesson, it seems to be about the nature of choosing a general-purpose format. By choosing Arrow, Parquet, and DataFusion, InfluxDB 3 got SQL, Flight SQL, and ecosystem tooling almost for free. That was a real win. But a general-purpose format is optimized for the general case, and the further your workload drifts from that average, the more you end up paying the difference somewhere. For InfluxDB 3, that difference was billed as a paid component named the compactor, and as rebuilding its own file format a year after GA.

When you're evaluating a technology choice, I'd suggest looking at this before the feature list in the release notes — what this system's fundamental constraint is, and which license the component that lifts that constraint sits behind.

References