QueryRecord 나 ConvertRecord 에 물린 CSVReader 가 다음 두 오류를 내는 경우의 원인과 처방이다. 둘 다 Apache Commons CSV 파서가 내는 메시지다.
java.io.IOException: (line 9080) invalid char between encapsulated token and delimiter
java.io.IOException: (startline 71) EOF reached before encapsulated token finished
첫째는 따옴표로 감싼 필드가 닫힌 뒤 구분자가 오기 전에 다른 문자가 끼었다는 뜻이고, 둘째는 여는 따옴표를 만났는데 닫는 따옴표 없이 파일 끝에 닿았다는 뜻이다.
.json 인데 CSV 오류가 난다면 Reader 타입부터 본다. Record Reader 가 CSVReader 인데 내용이 JSON 이면 따옴표 짝이 맞을 리 없다. 컨트롤러 서비스의 이름이 아니라 타입을 확인하고 JsonTreeReader 로 바꾼다.head -c 200 sample.csv; file sample.csv
sed -n '65,80p' sample.csv | cat -A
awk '{c+=gsub(/"/,"\"")} c%2 {print NR": 따옴표 누적="c}' sample.csv | head # 짝이 깨지는 첫 행
head -c 3 sample.csv | xxd # BOM 확인
resourceClaim / offset / length 가 같으면 같은 콘텐츠가 복제돼 두 프로세서로 간 것이므로 Reader 설정 한 곳만 고치면 된다.| 방안 | 평가 |
|---|---|
Reader 를 JsonTreeReader 로 교체 |
내용이 JSON 이면 이것이 정답 |
소스에서 CSV 를 거치지 않고 Avro/Parquet 직행 (ExecuteSQLRecord → PutParquet) |
근본 해결. 이스케이프 개념 자체가 없어진다 |
구분자를 제어문자(\u0001)로, Quote Mode NONE, Escape 비움 |
CSV 유지 시 차선. Writer 도 동일 설정 필요, 원천에서 \n \r 제거 전처리 필요 |
Quote " + Writer Quote All Values + Escape 비움 |
Reader/Writer 짝을 표준으로 정렬할 때 |
Quote/Escape 를 백틱이나 ~ 로 변경 |
권장하지 않음 (아래) |
로그 데이터처럼 인용 기능이 애초에 필요 없는 구조(구분자만으로 필드가 갈리는 경우)라면 Quote/Escape 를 데이터에 절대 나오지 않는 제어문자(\u0001 / \u0002)로 두어 인용 처리를 사실상 무력화하는 것이 가장 안전하다. UI 에서 제어문자 입력이 어려우면 ~, ^, ¶ 같은 문자를 쓰되 URL 이나 JSON 이 섞이는 PG 는 ~ 가 데이터에 들어올 수 있으므로 피한다.
Quote 문자를 읽는 쪽만 바꾸면 RFC 4180 대로 " 로 감싸 만든 원본은 " 가 평범한 글자가 되어 값 안의 콤마에서 컬럼이 밀린다. 예외는 나지 않고 잘못된 데이터가 조용히 적재되므로 배치에서 가장 나쁜 실패 형태다. Commons CSV 는 Quote 와 Escape 를 같은 문자로 두면 컨트롤러 서비스 Enable 단계에서 IllegalArgumentException 을 낼 수 있다. Escape 기본값 \ 는 데이터의 백슬래시(윈도우 경로, 정규식)와 충돌하는 사고가 잦으므로 비우는 편이 낫다.
QueryRecord 의 failure 를 자기 자신으로 되돌리면 영구 실패 FlowFile 이 무한 루프를 돈다. 별도 격리 큐로 보내거나 앞단에 ValidateRecord 를 둔다.SplitText 로 CSV 를 자르면 여러 줄짜리 인용 필드 한가운데가 잘린다. 분할은 SplitRecord 를 쓴다.\ 를 쓰는 Reader 에서 C:\path\" 같은 값은 뒤 따옴표를 무력화해 짝이 깨진다.