openlang

parse()에서 ImportFailure를 만드는데 Main에는 안 와요 (exit 2 대신 exit 3이 나와요)

안녕하세요,

지금 이직 사이에 있어서 100줄짜리 프로그램에 헷갈릴 시간은 넘치게 많아요.

ledger-import는 세미콜론 CSV를 읽어서 정리된 행을 staging 파일에 써요. 파일 전체가 파싱되지 않으면 staging에 아무것도 남아 있으면 안 돼요. 테스트 파일 data/march.csv는 4번째 줄에 금액이 깨져 있어요 (-12,5x). 저는 import rejected: line 4: bad amount ...가 나오고 exit code 2가 될 거라고 생각했어요. 그런데 실제로는 아래처럼 나오고 exit code는 3이에요:

SEVERE: staging problem: staging file build/staging.tmp closed with 2 uncommitted rows

줄 번호도, 잘못된 값도 어디에도 안 나와요. parse()의 catch 블록에 임시로 System.err.println을 넣으면 출력은 되니까 ImportFailure는 분명히 만들어지고 있어요. 다만 Main까지 도착하지 않을 뿐이에요. 4번째 줄을 고치면 import는 잘 돼요.

처음엔 Main의 catch 순서와 logging.properties 설정을 의심했어요. 두 catch 블록의 순서를 바꿔 봤지만 쓸 만한 변화는 없었어요. 관련 부분은 이래요:

// Importer.run()
StagingWriter writer = new StagingWriter(staging);
try {
    ...
    writer.add(parse(lineNo, line));
    ...
    writer.commit();
    return writer.count();
} finally {
    writer.close();
}
// StagingWriter.close()
out.close();
if (!committed && rows > 0) {
    Files.deleteIfExists(path);
    throw new IOException("staging file " + path + " closed with " + rows + " uncommitted rows");
}

Main은 ImportFailure를 먼저 catch하고(exit 2), 그다음 IOException을 catch해요(exit 3).

혹시 로깅은 아예 문제가 아닐 가능성이 있을까요? 제 예외는 어디로 가는 건가요?

영어에서 번역원문 보기

이어지는 대화 1

서로의 근거를 따라
  1. 고유 링크

    ImportFailure는 로깅 때문에 사라진 게 아니에요. finally 블록이 그걸 대체해 버려요.

    parse()가 ImportFailure를 던져요. 그다음 finally에서 writer.close()가 실행돼요. 행이 커밋되지 않은 상태라서 close()가 IOException을 던져요. finally에서 새로 던져진 예외가 이겨요. ImportFailure는 버려지고, Main은 IOException만 보게 돼요. 그래서 exit 3이 나오는 거예요.

    try-with-resources로 고치세요. 본문에서 예외가 던져지면 close()에서 나온 예외는 suppressed로 추가되고, 원래 예외가 primary로 남아요:

    try (StagingWriter writer = new StagingWriter(staging)) {
        ...
        writer.commit();
        return writer.count();
    }

    StagingWriter는 AutoCloseable을 구현해야 해요. 나중에 "uncommitted" 검사도 close() 밖으로 빼세요. 파일을 지우기만 하는 abort 경로가 더 깔끔해요.

    영어에서 번역원문 보기

같은 질문의 다른 갈래

  1. `String::from_utf8`는 실패하면 바이트를 돌려준다

    값을 소비하는 API는 실패했을 때 그 값이 어떻게 되는지 정해야 합니다. String::from_utf8는 Vec<u8>를 값으로 받고, 실패하면 오류가 그것을 소유합니다. 출력은 bad byte at 2, 4 bytes…

    답글 0최근 활동 rust
  2. except ValueError가 gather(return_exceptions=True) 주변에서 실행되지 않고, ValueError 때문에 TypeError가 나요

    클라이언트 체크인 전에 작은 스크립트를 돌려서 각 클라이언트 사이트를 세 가지 지표로 점수를 매기는데, 클라이언트별 가중치는 해당 클라이언트의 analytics 엔드포인트에서 가져와요. 가져오는 부분은 return_excep…

    답글 5최근 활동 python
  3. use 체인 줄마다 붙는 `result.map_error`

    use와 result.try 조합은 Gleam에서 제일 좋아하는 부분인데, 요즘 조금 미워지기 시작했어요. 단계마다 다른 모듈을 부르고 모듈마다 자기 오류 타입이 있으니, 줄마다 result.map_error를 달고 다녀요.…

    답글 1최근 활동 gleam