개발/NodeJS

pnpm 심볼릭 링크 버그 수정하기

bitofsky 2024. 2. 26. 12:12

개요

최근 내부 저장소의 기본 의존성을 최신 버전으로 업그레이드한 이후, 특정 서버가 런타임 중에 중단되기 시작했다.

ERROR 1: uncaughtException : Error: libtensorflow.so.2: cannot open shared object file: No such file or directory

TensorFlow의 Node.js 버전은 모듈 설치 후 설치 스크립트에 의해 운영 체제별로 의존성을 다운로드하거나 소스 코드에서 빌드되며, 결국 빌드된 바이너리 파일을 심볼릭 링크로 연결한 특정 경로를 생성한다. TensorFlow Node.js API는 이러한 심볼릭 링크를 참조한다.

이 모듈의 내부 작동 방식은 사용자가 알 필요가 없어야 한다. 정상적으로 빌드가 완료되면 그냥 작동해야 하지만, 문제가 발생하면 사용자도 이를 알게 된다.

pnpm을 사용하여 모듈을 처음 설치할 때, 모든 파일과 그 인덱스를 캐시 저장소에 기록한다. 두 번째 설치부터는 이 인덱스를 사용하여 파일을 복원한다. 그러나 pnpm 8.7 버전부터 심볼릭 링크된 파일이 인덱스에 포함되지 않는 문제를 발견했다.

이로 인해 CI/CD 빌드 런타임에서 캐시가 없는 상태로 처음 설치할 때는 심볼릭 링크가 생성되어 정상적으로 작동하지만, 빌드 캐시에서 복원하는 두 번째 설치부터는 심볼릭 링크가 누락되어 런타임 오류가 발생한다.

이 문제를 pnpm에 보고했다.

https://github.com/pnpm/pnpm/issues/7691

그러나 비슷한 이슈는 이전부터 존재했고, 아직도 해결되지 않고 있으며 담당자도 뭔가 삽을 푸고있는 것으로 보였다. 이런게 직접 나설 때이다.

 

이슈 추적

문제의 원인은 심볼릭 링크가 .json 파일 인덱스에 기록되지 않는 것이었다. 따라서 심볼릭 링크를 기록하는 부분을 조사하기로 했다.

pnpm 프로젝트를 다운로드하여 문제를 추적하기 시작했다. 모듈 설치가 완료된 후 내부 디렉토리를 캐싱하는 코드를 찾았다.

해당 코드는 /store/cafs/src/addFilesFromDir.ts에 위치해 있었다.

테스트 케이스를 작성하여 문제를 재현해보았다.

# test directory 설정
~/pnpm/store/cafs/test/fixtures/include-symlink$ ls -li
10272393 lrwxrwxrwx 1 bitofsky bitofsky 10 Feb 23 16:09 link.txt -> origin.txt
10272392 -rw-rw-r-- 1 bitofsky bitofsky  2 Feb 23 16:06 origin.txt

# jest test 추가
test('recognize files in a directory with symbolic links', () => {
  const storeDir = tempy.directory()
  const srcDir = path.join(__dirname, 'fixtures/include-symlink')
  const addFiles = () => createCafs(storeDir).addFilesFromDir(srcDir)

  const { filesIndex } = addFiles()

  expect(filesIndex['origin.txt']).toBeDefined()
  expect(filesIndex['link.txt']).toBeDefined()
})

버전 8.6에서는 테스트가 통과했지만, 8.7 이후 버전에서는 실패했다. 반면 9.x 알파 버전에서는 다시 통과했다.

문제 해결

addFilesFromDir에서 파일 인덱스 목록을 반환하는 과정에 문제가 있음을 확인했다.

버전별 코드 변경 사항을 검토했고, 문제를 식별한 후, 9.x 알파 버전에서 이미 해결된 사실을 확인했다.

그러나 해결이 이 문제를 해결하기 위함이 아니라 코드 개선중 얼떨결에 해결된 것으로 보이며 문제 인식이 없는 한 하위 버전에 자동으로 백포트 하지는 않을 것 같았다.

따라서 8버전에도 이 수정 사항을 백포트하면 될것으로 판단해 pr을 올리는 대신 코멘트를 작성하기로 했다.

https://github.com/pnpm/pnpm/issues/7691#issuecomment-1961697431

 

pnpm store doesn't have symlink files after version 8.7 · Issue #7691 · pnpm/pnpm

Verify latest release I verified that the issue exists in the latest pnpm release pnpm version v8.7.0~ Which area(s) of pnpm are affected? (leave empty if unsure) Store Link to the code that reprod...

github.com

근본 원인은 nodejs fs 내장 모듈에서 제공하는 readdir과 stat 메소드에서 반환하는 파일의 속성을 조사하는 객체의 차이에서 발생했다.

readdir에서 withFileTypes: true를 주고 디렉토리를 읽으면 디렉토리 내부의 파일 목록을 제공하는데 이 파일 각각은 Dirent object이다.

그러나 stat 메소드에서 해당 파일을 직접 읽으면 Stats object가 반환된다.

8.7 이전에선 readdir() → stat() 과정을 통해 Stats object로부터 isFile() 을 체크했는데 이 메소드는 심볼릭 링크를 true로 반환했다.

8.7 이후에선 성능 개선 목적 때문인지, readdir( withFileTypes: true ) 로 하고 반환되는 file Dirent object로부터 isFile()을 체크했는데, pnpm 개발자가 간과한 것은 심볼릭 링크된 파일의 경우 여기서 false가 반환된다는 점이다. 그래서 8.7 이후부터는 심볼릭 링크된 파일이 사라진 것이었다.

해당 이슈에 대한 담당자의 인지 후, 수정된 버그 픽스가 곧 배포되었다.

이 버그는 오랫동안 지속되었으나, 캐시가 생성된 상태에서 버전 업데이트가 이루어지면 심볼릭 링크가 이미 인덱스에 기록된 캐시를 재사용하기 때문에 새로운 빌드 캐시에서만 문제가 발생해 명확하게 드러나지 않았던 것으로 보인다.

오늘도 지옥불에서 해피 코딩

'개발 > NodeJS' 카테고리의 다른 글

Typescript compilerHost를 사용해 컴파일 할 때 주의사항  (0) 2024.03.20