• LINKS 1.0.13 — 결함 수정 wave 1·ARCA 0.1.21·migration 0003·RGSS 지연 콘텐츠

    LINKS 1.0.13

    이 릴리스는 이전 릴리스 1.0.12(8843007119) 위에 결함 수정 wave 1, ARCA 0.1.21 pin, schema migration 0003, RGSS 지연 콘텐츠·선부팅을 더했습니다. 최종 소스는 e43a2034b2, 루트 패키지는 1.0.13, ARCA pin은 exact 0.1.21입니다. schema가 바뀌어 migration 1개가 적용되며, 적용 뒤에는 이전 image(1.0.12)로 되돌릴 수 없습니다.

    업그레이드: 1.0.12 → 1.0.13은 sh update.sh만 실행합니다

    1. 갱신 방법: 배포 디렉터리에서 sh update.sh만 실행합니다. reset.sh는 실행하지 않습니다.
    2. migration 0003 적용: update.sh가 [4/6] Checking the database schema version 뒤에 Pending migrations: 1 한 줄을 출력하고, [5/6]의 up 때 migration service가 0003_job_failure_handling을 적용합니다(schema f7fe92f3… → d28acb3f…). catalog의 /readyz는 적용이 끝난 뒤 준비됩니다. 성공하면 Updated to 1.0.13 (catalog a718b2dc9b42), runtime.package ready.로 끝납니다.
    3. 이전 image로 복귀 불가: migration이 적용된 DB는 1.0.12 image의 사슬 밖(mismatch)입니다. 1.0.12를 rollback pin(LINKS_REGISTRY_TAG=1.0.12@sha256:…)으로 지정하면 update.sh가 [4/6]에서 Run: sh reset.sh로 멈추고 아무것도 바꾸지 않습니다. 이 안내를 따르지 마십시오(데이터를 지우는 초기화입니다). pin을 비우고 sh update.sh를 다시 실행하면 1.0.13으로 돌아옵니다. 문제가 생기면 이전 image로 되돌리지 말고 원인을 고쳐 다시 갱신하거나 수정 release를 기다립니다. LINKS는 backup을 제공하지 않습니다.
    4. 배포 파일 변경: update.sh가 받는 5개 파일 중 compose.yaml(worker service의 stop_grace_period: 30s와 LINKS_WORKER_SHUTDOWN_GRACE_SECONDS, package one-shot의 DB pool 2)과 ops/runtime-package-coordinator.sh(Builder image를 검증된 두 digest로만 선택)가 바뀌었고 update.sh가 이 둘을 설치합니다. update.sh(ops/deploy-registry.sh), reset.sh(ops/reset-registry.sh), infra/docker/Caddyfile은 1.0.12와 byte 단위로 같습니다. .env.example에는 LINKS_WORKER_SHUTDOWN_GRACE_SECONDS=20이 추가됐지만 Compose 기본값이 같은 20이라 .env.production에 할 일은 없습니다.
    cd /volume1/docker/links
    sh update.sh
    

    기존 VX/VX Ace 게임은 "준비 필요"로 바뀝니다. ARCA release manifest와 Builder digest가 달라졌기 때문입니다. 로그인 사용자가 다시 준비하면 새 실행 버전(generation)이 만들어지고(서버에 보관된 게임 object는 재사용), 이 generation은 지연 모드(필요한 부분만 Range로 받기·선부팅·시작 버튼)로 실행됩니다. 브라우저는 처음 한 번 게임을 다시 받으며, chunk cache는 artifact 기준이라 그 뒤에는 다시 받지 않습니다. 기존 세이브는 그대로 열립니다. MV/MZ 게임은 영향이 없습니다. RTP 재준비·설정 변경·DB 작업·artifact 삭제 같은 운영자 작업은 없습니다. 격리 gate에서 postgres container는 다시 만들어지지 않았습니다. 원본 game library는 읽기 전용으로 유지합니다. 이번 게시 작업에서 운영 NAS 배포나 DB 초기화는 실행하지 않았습니다.

    변경 내용

    결함 수정 wave 1(개발 계획 9.1)

    • 스캐너: 거부 이름 cursor, frame 깊이 상한 256, entry realpath 실패 격리, PE 서명 상한을 companion directory별로 적용, 하위 sidecar 경계 귀속과 유효 sidecar만의 경계 선택, 완료 후보 loop의 후보별 발행(정규화 충돌 보존, 경로 충돌 warning 1회), 후보별 발행의 VNDB lookup을 scan 성공 transaction에서 100개 batch로 접수.
    • API·launch broker: 발급 거부를 trigger별 code(PLAY_SESSION_NOT_READY·PLAY_SESSION_PREPARATION_REQUIRED 등)와 DB 장애 503으로 구분, SSE 종료, 429·validation·413 응답, 세션 조회를 SELECT + 5분 touch로, body 64 KiB 상한.
    • web: 세션 부팅·로그인 세대별 401 처리, 화면 오류 경계와 root 오류 요약 log, Player 세션 종료(DELETE)에서 저장 보존, 게스트 자체 종료, timer NaN·peer 알림·스크롤·Back·초점·title 결함.
    • 메타데이터·번역: 이미지 루프 heartbeat, 요약 머지 우선순위·태그 상한, 페이지 cool-down 전파, 결정적 거부만 즉시 종료, 공급자·DB 일시 장애는 deferJobAttempt로 미룸, VNDB lookup이 새 circuit 기간만큼 미룸.
    • 런타임: build 시작 시 source 재검사, heartbeat 재시도, CAS object·generation과 store root까지 fsync, DB 시계 사용, Player /session의 expiresInSeconds.
    • job: 최종 실패 cascade, 재시도 지수 backoff, deferJobAttempt, role별 DB pool 기본값, worker crash handler, 종료 유예(LINKS_WORKER_SHUTDOWN_GRACE_SECONDS)와 Compose stop_grace_period, 의존 연결 요소 잠금과 잠금 경합 재시도.

    Schema migration 0003

    • 0003_job_failure_handling: job 최종 실패 cascade trigger(기다리던 queued 자손 job을 같은 transaction에서 DEPENDENCY_FAILED로 닫음), jobs.deferral_count, 의존 연결 요소 행을 id 순서로 잠그는 함수 links_lock_job_dependency_component. 기준 schema(infra/schema/030_jobs.sql)도 같은 변경을 담습니다.

    ARCA 0.1.21 pin

    • Builder manifest digest와 config digest를 release pin에 결속하고, coordinator가 Builder image ID를 이 두 digest로만 선택합니다(tag fallback 제거).
    • RGSS frame launch에 presentation(nearest/fit)을 전달하고 보고합니다. letterbox oracle은 기대 geometry와 대조합니다.

    RGSS 지연 콘텐츠·선부팅

    • game·RTP에 chunk digest가 있는 generation은 1 MiB chunk를 HTTP Range로 읽어 부모의 OPFS content-addressed chunk cache에 두고, 타이틀까지 선부팅한 뒤 부모의 시작 버튼을 기다립니다. chunk digest가 없으면 기존 heap 경로로 실행합니다.
    • Player artifact 요청의 세션 조회를 5초 재사용하고, 보관 object의 archive에서 chunk digest를 계산해 host launch에 싣습니다(derived chunks).
    • 자동 재생이 허용되지 않은 브라우저에서는 preboot:false로 시작 클릭 때 부팅합니다. ARCA 0.1.21이 suspended AudioContext 정체를 고쳐 강제 선부팅 READY가 3.6~3.7초입니다.

    세션 남은 시간·VNDB

    • 세션 남은 시간을 DB가 계산한 값(sessionExpiresInSeconds)으로 broker → Player → 부모에 전달하고, 만료 안내·게스트 만료 종료를 받은 시각 + expiresInSeconds로 정합니다.
    • VNDB lookup circuit 지연, 완료 경로의 lookup batching, 직접 begin transaction의 잠금 경합 재시도 경계.

    검증과 남은 범위

    • PASS: 브랜치 gate(고유 Compose project, BuildKit cache 없는 frozen install)
      • 설치본 identity: ARCA 0.1.21 release manifest 219cfa29…, Builder manifest digest sha256:83adbc29…, config digest sha256:43bc702e…, arca-release verify, npm integrity sha512-MA28Kw…(lockfile·registry metadata·registry tarball·ARCA 배포 tarball 일치).
      • static(Prettier·ESLint·typecheck·docs-static), 전체 unit 175 files·1,468 tests, schema 36 files·273 tests(빈 DB fresh 적용과 migration 적용 결과의 구조 등가 포함), 문서 검사, git diff --check, deploy 기록기 96·reset 기록기 30 case, Compose config 3조합과 Watchtower label, foundation.
      • arca-npm-check(+ 3 files·42 tests), RGSS release-set·runtime-binding(67 files)·Builder 입력 5 case·Builder와 import 통합(합성 VX Ace: digest로 고정한 Builder, chunks game 3, 보관 object reused-derived-equals-builder).
      • run-player-lazy(Chromium, 자동 재생 정책을 강제한 Chromium, Firefox), Player DPR·터치 패드(Chromium·Firefox), sandbox-rgss, Player 재시작(Chromium·Firefox), browser acceptance mz-chromium(12개 단언, 공유 링크 게스트 실행).
    • PASS: 릴리스 gate(Compose v2.20.1, NAS 조건)
      • 환경: 격리 docker:24.0.2-dind, 공식 Docker Compose v2.20.1 binary(게시 checksum 일치), stager 1026:100, ./data bind store, library 최상위 0:0 000 + 1026 ACL, 합성 HTTPS release 서버와 합성 TLS registry.
      • 1.0.12 설치에서 합성 MZ ready build, ARCA 0.1.20 Builder로 만든 합성 VX Ace ready build(chunks 포함), 로그인 사용자 세이브(MZ·VX Ace).
      • 설치된 1.0.12 sh update.sh → 1.0.13: exit 0(53초), Pending migrations: 1, migration 0003 적용(links_schema_version d28acb3f…, 이력에 0003, jobs.deferral_count·잠금 함수 존재), /readyz 200, compose.yaml과 coordinator script 2개만 설치, 12개 service network 불일치 0·unhealthy 0·모두 Watchtower 제외, readiness PASS, catalog·Player 1.0.13·ARCA 0.1.21, worker stop_grace_period 30초·종료 유예 20초, postgres 재생성 없음.
      • Builder image rgss23-builder:0.1.21 = config digest sha256:43bc702e…(Docker 24), 준비 중 Builder container가 그 digest로 생성됨(tag 사용 없음).
      • VX Ace가 "준비 필요"로 바뀌고, 준비 요청 → 0.1.21 새 generation. 1.0.12의 game object를 재사용하고 chunks를 유지, Player가 chunk Range 요청에 206(Content-Range, chunk digest 일치)으로 답함. 1.0.12에서 만든 세이브가 새 generation에서 200(같은 ETag·bytes, 이전 build 출처).
      • 실제 web app·Player를 Chromium으로 실행: frame data-content-mode=lazy, 선부팅 뒤 시작, 게임 읽기는 206 Range 1회뿐(전체 GET 0), OPFS chunk cache에 저장. 다시 열면 게임 byte를 하나도 받지 않음(cache hit).
      • 1.0.13에서 처음 준비한 두 번째 합성 VX Ace도 chunks 유지·Range 206. MZ build·세이브 불변. 재-update exit 0(13초), 변경 없음.
      • 1.0.12 rollback pin은 schema mismatch로 [4/6]에서 거부되고 container·schema·postgres가 바뀌지 않음. pin을 비운 update.sh로 복구(coordinator 재기동).
    • 알려진 제한·NOT RUN: 개발 계획 9.1 잔여 3건(MV/MZ 저장 단일 slot 1 MiB, web-static 검사의 주석 host 수집, 운영 memory·pid·shm 한도), R65c P3 후속(Player 세션 cache 시계, host-launch 실패 memo, 지연 경로 letterbox 측정 등, 개발 계획 9.2), 실제 망(느린 모바일)·느린 OPFS·실제 다중 탭 부하, 실제 모바일 기기, 실제 VX/VX Ace 게임과 실제 RTP, 실제 오디오 출력, 실제 NAS 갱신.

    배포 이미지와 파일

    • Git annotated tag는 1.0.13이고 최종 소스는 e43a2034b2입니다(branch release-1.0.13).
    • 단일 linux/amd64 이미지: git.2ex.dev/hanyul/links:1.0.13 = git.2ex.dev/hanyul/links:latest
    • 원격 digest: sha256:a718b2dc9b42742bc418739a44c7231097da669873e9cdbc1c7605cfee931068
    • release asset은 없습니다. update.sh가 tag 1.0.13의 5개 배포 파일을 HTTPS로 받습니다. 대응은 update.sh=ops/deploy-registry.sh, reset.sh=ops/reset-registry.sh이고, 나머지는 compose.yaml·infra/docker/Caddyfile·ops/runtime-package-coordinator.sh입니다.
    Downloads 2