Python ossfs 라이브러리를 사용하여 파일에 데이터를 추가할 때 발생하는 오류 해결

Python의 ossfs 라이브러리를 사용하여 파일에 데이터를 추가(append)하는 과정에서 오류가 발생했습니다. 이 문제는 ossfs가 파일을 덮어쓰려고 할 때 내부적으로 발생하는 동작 방식과 관련이 있습니다. 문제 해결 과정을 공유하여 비슷한 상황에 놓인 다른 사용자들에게 도움을 드리고자 합니다.

문제 상황 재현

오류 로그는 다음과 같습니다:

File "/data3/maxiaoyong/projects/corpus-projects/knowledge_tagging/venv/lib/python3.12/site-packages/ossfs/core.py", line 482, in append_object
    result = self._call_oss(
             ^^^^^^^^^^^^^^^
  File "/data3/maxiaoyong/projects/corpus-projects/knowledge_tagging/venv/lib/python3.12/site-packages/ossfs/core.py", line 106, in _call_oss
    raise translate_oss_error(error) from error
OSError: [Errno 5] {'status': 409, 'x-oss-request-id': '687E057AEDE53B3936EFE981', 'details': {'Code': 'PositionNotEqualToLength', 'Message': 'Position is not equal to file length', 'RequestId': '687E057AEDE53B3936EFE981', 'HostId': 'center.oss-cn-hangzhou-zjy-d01-a.ops.cloud.zhejianglab.com'}} ((20250721182245-leegumj "*"))

오류 메시지 PositionNotEqualToLength는 OSS(Object Storage Service)에서 파일의 현재 길이와 요청된 쓰기 위치가 일치하지 않아 발생하는 409 Conflict 오류입니다. 이는 ossfs 라이브러리가 파일을 덮어쓸 때 (overwrite=True) 내부적으로 append_object 메서드를 호출하려다 발생하는 문제입니다. 만약 파일이 이미 존재하면, ossfs는 파일의 현재 크기를 기준으로 쓰기 위치를 지정하려 하는데, 이 위치가 실제 파일 크기와 다를 경우 위와 같은 오류가 발생합니다.

사용자의 코드에서 문제가 발생하는 부분은 다음과 같습니다:


# ... (이전 코드) ...
fs.write_text(args.task_status_path, json.dumps(task_status), encoding='utf8', overwrite=True)
# with fs.open(args.task_status_path, 'w', encoding='utf8') as f:
#     json.dump(task_status, f)
# ... (이후 코드) ...

fs.write_text(..., overwrite=True)는 파일이 존재하면 append_object를 시도합니다. 만약 이전에 실행된 쓰기 작업이 완료되기 전에 다음 쓰기 작업이 시도되면, OSS가 인식하는 파일 크기와 ossfs가 기록한 크기 사이에 불일치가 발생하여 409 오류가 발생할 수 있습니다.

해결 방안

이 문제를 해결하기 위한 가장 확실한 방법은 append_object 동작을 피하는 것입니다. 즉, 파일을 추가하는 대신 완전히 새로 쓰는 방식을 사용하는 것입니다.

방법 1: 기존 파일 삭제 후 새로 작성

overwrite=True 옵션을 사용하는 대신, 쓰기 전에 해당 파일이 존재하면 먼저 삭제하는 방식입니다.


if fs.exists(args.task_status_path):
    fs.rm(args.task_status_path)  # 기존 파일 삭제
fs.write_text(args.task_status_path,
              json.dumps(task_status, ensure_ascii=False),
              encoding='utf-8')  # overwrite=False가 기본값이므로 새로 작성됨

방법 2: overwrite=False 사용 (최신 버전 ossfs)

최신 버전의 ossfs 라이브러리를 사용한다면, overwrite=False를 명시하여 append_object 동작을 강제로 비활성화할 수 있습니다.


fs.write_text(args.task_status_path,
              json.dumps(task_status, ensure_ascii=False),
              encoding='utf-8',
              overwrite=False) # append_object 동작 방지

추가 문제: fs.exists()의 부정확성

위의 해결 방법을 적용했음에도 불구하고 fs.exists()가 False를 반환하지만 실제로는 파일이 존재하고 쓰기 시 오류가 발생하는 경우가 있습니다. 이는 ossfs가 로컬 캐시에 파일 목록을 저장하고 있으며, 이 캐시가 실시간으로 갱신되지 않아 발생하는 현상입니다. 특히 분산 환경이나 OSS의 최종 일관성(eventual consistency) 모델 때문에 이런 문제가 두드러질 수 있습니다.

증상

  1. 첫 번째 루프: write_text(..., overwrite=True) 실행 시 append_object 시도, OSS에서 409 오류 발생. (이 오류가 외부 try-except 블록에 의해 처리되어 눈에 띄지 않을 수 있음)
  2. 두 번째 루프: fs.exists()는 로컬 캐시를 확인하므로 파일이 없다고 판단 (False 반환). write_text(..., overwrite=True) 다시 실행 시 append_object 시도, 또 다시 409 오류 발생하며 최종적으로 예외가 외부로 던져짐.

검증 방법

오류 발생 지점에서 다음과 같은 로그를 추가하여 확인할 수 있습니다.


print("fs.exists() 캐시 결과:", fs.exists(args.task_status_path))
print("실제 listdir 결과:", fs.listdir(os.path.dirname(args.task_status_path)))

fs.exists()는 False를 반환하지만, fs.listdir() 결과에는 해당 파일이 존재하는 것을 확인할 수 있습니다.

근본적인 해결책

  1. 캐시 강제 갱신: ossfs의 invalidate_cache() 메서드를 사용하여 캐시를 강제로 갱신합니다.
  2. 
    fs.invalidate_cache(args.task_status_path) # 특정 파일 또는 디렉토리의 캐시 갱신
    # 또는 fs.invalidate_cache() # 전체 캐시 갱신
            
  3. append_object 동작 회피: 방법 1 (삭제 후 재작성) 또는 방법 2 (`overwrite=False`)를 사용합니다.
  4. 
    try:
        fs.rm(args.task_status_path) # 파일 존재 여부와 관계없이 삭제 시도
    except FileNotFoundError:
        pass # 파일이 없어도 정상 처리
    fs.write_text(args.task_status_path,
                  json.dumps(task_status, ensure_ascii=False),
                  encoding='utf-8')
            
  5. overwrite=True 사용 시 주의: 최신 버전의 ossfs는 overwrite=True 시 기본적으로 PutObject를 사용하지만, 여전히 캐시 문제의 영향을 받을 수 있습니다.

요약

  • fs.exists()가 False를 반환하는 것은 로컬 캐시를 읽기 때문이며, 실제 OSS에는 파일이 존재할 수 있습니다.
  • 409 PositionNotEqualToLength 오류는 ossfs의 overwrite=True 옵션이 내부적으로 append_object를 시도할 때 파일 크기 불일치로 인해 발생합니다.
  • 해결책은 append_object 동작을 피하는 것입니다. 즉, invalidate_cache()를 사용하거나, 파일을 먼저 삭제하고 새로 작성하는 방식을 사용해야 합니다.

태그: ossfs python aws s3 aliyun oss Object Storage

9월 26일 03:58에 게시됨