openlang

업그레이드 가능하다는 건 스토리지 레이아웃이 영원하다는 뜻입니다

프록시 패턴이라는 말을 들으면 불변성 문제가 사라졌다고 생각하는 사람이 많습니다. 사라진 게 아니라 옮겨 갔습니다. 프록시가 상태를 들고 delegatecall로 호출을 넘기므로 구현 쪽 코드는 프록시의 스토리지 위에서 돕니다. 이후의 모든 구현은 이전의 모든 구현과 어느 슬롯에 무엇이 있는지 합의해야 합니다.

상태 변수는 선언 순서대로 배치되고, 들어갈 수 있으면 32바이트 슬롯 하나에 채워 넣으며, 상속된 변수가 먼저 옵니다. 중간에 필드를 끼우거나, 기반 컨트랙트 순서를 바꾸거나, 다른 값과 함께 채워진 uint128을 uint256으로 넓히면 새 코드는 옛 바이트를 새 의미로 읽습니다. 컴파일러는 옛 레이아웃이 있다는 사실을 모릅니다. 새 소스를 첫 배포인 것처럼 컴파일할 뿐입니다.

생성자도 도움이 안 됩니다. 생성자는 구현 자체를 배포할 때 구현의 스토리지를 대상으로 실행됩니다. 그래서 프록시는 이니셜라이저 함수에 기대고, 그 함수가 한 번만 돌도록 막아 두어야 합니다.

제 규칙은 추가만 하는 것입니다. 리뷰에서는 로직 diff를 읽기 전에 이전 버전과 새 버전의 스토리지 레이아웃부터 비교합니다. 로직 diff는 쉬운 쪽입니다.

이어지는 대화 2

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

    참고로 말씀드리면, 모든 것을 제자리에서 바꿀 수 있다고 더 나은 것도 아닙니다. ABAP 딕셔너리의 도메인이나 데이터 요소는 바꾸고 활성화할 수 있으며, 그것을 TYPE으로 참조해 변수를 선언한 프로그램은 다음에 다시 생성될 때 새 정의를 따릅니다. 누구도 다시 배포하지 않습니다. 친절해 보이지만, 그 필드를 손으로 쓴 TYPE c LENGTH 10에 옮겨 담던 오래된 리포트가 아무 말 없이 값을 잘라 내기 시작하면 이야기가 달라집니다. 테이블 정의가 바뀌면 새 정의를 쓰기 전에 데이터베이스 테이블 변환이 필요할 수도 있고, 옛 데이터가 새 의미를 만나는 곳이 바로 그 단계입니다.

    그래서 추가만 한다는 규칙에 동의하고, 리뷰에 한 가지를 더하고 싶습니다. 옛 모양을 손으로 베껴 둔 곳을 전부 찾아보십시오. 그런 사본은 딕셔너리를 따라가지 않고, 깨짐은 거기에 숨어 있습니다.

  2. 고유 링크

    이전 호출 코드도 옆에 놓고 같이 보죠. 다만 여기서 이전 호출자는 저장된 상태 그 자체예요. 레이아웃 비교는 아무것도 움직이지 않았다는 것까지만 알려 줘요. 안 쓰는 것처럼 보이는 슬롯이 왜 그 자리에 그대로 있어야 하는지는 나중에 읽는 사람에게 말해 주지 못하니, 릴리스마다 코드 옆에 적어 두고 싶어요.