Ctrl K
ComunidadGuías…

docker-compose.concurrency-e2e.yml

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Multi-replica single-flight concurrency harness (#1624).
#
# Reproduces #1606: per-process singleflight races across replicas serving
# partial/truncated artifacts. Acceptance gate for #1609.
#
# Topology:
#   postgres        - one shared PostgreSQL (all replicas share metadata)
#   minio + init    - one shared S3-compatible object store (all replicas share
#                     blobs, so a blob cached by ONE replica is visible to ALL)
#   mock-upstream   - controllable upstream serving ONE large artifact with a
#                     known SHA-256, injectable latency, and a fetch counter
#   backend         - >=3 replicas (scale via --scale backend=N), all pointed at
#                     the SAME postgres + minio
#   lb              - nginx round-robin in front of the backend replicas
#
# Usage (see scripts/concurrency/README.md):
#   docker compose -f docker-compose.concurrency-e2e.yml build
#   docker compose -f docker-compose.concurrency-e2e.yml up -d \
#       --scale backend=3
#   ./scripts/concurrency/run-singleflight-race.sh
#   docker compose -f docker-compose.concurrency-e2e.yml down -v
#
# NOTE: this harness is RED on `main` by design (it reproduces #1606). It is NOT
# wired into the per-PR gate. It flips green once #1609 (cross-process
# single-flight) lands and then becomes its regression gate.

services:
  postgres:
    # ci-mirror copy (see the minio services below).
    image: ghcr.io/artifact-keeper/ci-mirror/postgres:18-alpine
    container_name: concurrency-e2e-db
    environment:
      POSTGRES_USER: registry
      POSTGRES_PASSWORD: registry
      POSTGRES_DB: artifact_registry
    tmpfs:
      - /var/lib/postgresql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U registry -d artifact_registry"]
      interval: 2s
      timeout: 5s
      retries: 15
    networks:
      - concurrency-e2e-network

  minio:
    # ci-mirror copy: Docker Hub no longer hosts minio/minio, and every image CI
    # depends on is pulled from ghcr.io/artifact-keeper/ci-mirror (#3949).
    image: ghcr.io/artifact-keeper/ci-mirror/minio:RELEASE.2025-09-07T16-13-09Z
    container_name: concurrency-e2e-minio
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: minioadmin
      MINIO_ROOT_PASSWORD: minioadmin
    volumes:
      - concurrency_e2e_minio_data:/data
    ports:
      - "9000:9000"
      - "9001:9001"
    healthcheck:
      test: ["CMD", "mc", "ready", "local"]
      interval: 5s
      timeout: 5s
      retries: 10
    networks:
      - concurrency-e2e-network

  minio-init:
    # ci-mirror copy of minio/mc (see the minio service above, #3949).
    image: ghcr.io/artifact-keeper/ci-mirror/mc:RELEASE.2025-08-13T08-35-41Z
    container_name: concurrency-e2e-minio-init
    depends_on:
      minio:
        condition: service_healthy
    entrypoint: >
      /bin/sh -c "
      mc alias set local http://minio:9000 minioadmin minioadmin &&
      mc mb local/artifact-keeper --ignore-existing &&
      mc anonymous set none local/artifact-keeper &&
      echo 'Bucket artifact-keeper created'
      "
    restart: "no"
    networks:
      - concurrency-e2e-network

  mock-upstream:
    build:
      context: ./scripts/concurrency
      dockerfile: Dockerfile.mock-upstream
    image: ak-concurrency-mock-upstream:test
    # No container_name: must be scalable-safe and we run a single instance.
    environment:
      # 256 MiB artifact, 1.5s latency. All overridable from the shell,
      # including FAIL_EVERY_NTH for fault-injection runs.
      ARTIFACT_SIZE_BYTES: "${ARTIFACT_SIZE_BYTES:-268435456}"
      ARTIFACT_NAME: "${ARTIFACT_NAME:-big-artifact.bin}"
      LATENCY_MS: "${LATENCY_MS:-1500}"
      PORT: "9999"
      SEED: "${SEED:-1624}"
      FAIL_EVERY_NTH: "${FAIL_EVERY_NTH:-0}"
      FAIL_MODE: "${FAIL_MODE:-truncated}"
    ports:
      - "19999:9999"
    healthcheck:
      test: ["CMD", "python3", "-c", "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:9999/healthz',timeout=2).read()==b'ok' else 1)"]
      interval: 5s
      timeout: 3s
      retries: 20
    networks:
      - concurrency-e2e-network

  backend:
    image: artifact-keeper-backend:test
    build:
      context: .
      dockerfile: docker/Dockerfile.backend
    # No container_name: compose appends a replica index when scaled.
    depends_on:
      postgres:
        condition: service_healthy
      minio-init:
        condition: service_completed_successfully
      mock-upstream:
        condition: service_healthy
    # THROWAWAY CREDENTIAL — NOT for deployment (#3490). Supplies ADMIN_PASSWORD
    # from .env.test, the one place the value is written; export
    # AK_TEST_ADMIN_PASSWORD before `docker compose up` AND before
    # run-singleflight-race.sh to use a generated value instead. See the longer
    # note in docker-compose.test.yml.
    env_file:
      - .env.test
    environment:
      DATABASE_URL: postgresql://registry:registry@postgres:5432/artifact_registry
      # SHARED S3 object storage: a blob cached by one replica is visible to all.
      STORAGE_BACKEND: s3
      S3_BUCKET: artifact-keeper
      S3_REGION: us-east-1
      S3_ENDPOINT: http://minio:9000
      S3_ACCESS_KEY_ID: minioadmin
      S3_SECRET_ACCESS_KEY: minioadmin
      STORAGE_PATH: /data/storage
      BACKUP_PATH: /data/backups
      PLUGINS_DIR: /data/plugins
      JWT_SECRET: concurrency-e2e-nonprod-amber-cobalt-river-7391
      # E2E-ONLY SSRF relaxation (#3363). The race driver creates a `remote`
      # repository pointing at `mock-upstream:9999` on this compose network;
      # that name resolves to an RFC1918 address, which the outbound-URL guard
      # (#1832) rejects, so `create_repo` returned HTTP 400 and the driver
      # exited 2 before generating any load. `concurrency-e2e-network` is a
      # plain bridge with no pinned subnet, so there is no stable CIDR to give
      # the narrower AK_SSRF_ALLOW_PRIVATE_CIDRS. Loopback, link-local and
      # cloud-metadata addresses stay hard-blocked regardless.
      UPSTREAM_ALLOW_PRIVATE_IPS: "true"
      # Cross-replica single-flight (#1609) ON. This harness exists to prove
      # that ONE replica cold-fetches a given object cluster-wide; with the
      # default `false` the stack under test runs only the per-process
      # coordinator, so assertion A1 (upstream fetch counter == 1) is measuring
      # a configuration in which cross-replica coordination is switched off and
      # cannot hold. The feature under test must be enabled in the stack under
      # test. Overridable from the shell so an A/B run can compare the two.
      # With it on, a replica that loses the lock blocks until the leader has
      # published and then reads the cache (#4013); only a leader that outlives
      # PROXY_SINGLEFLIGHT_LOCK_WAIT_TIMEOUT_SECS (default 65) makes the others
      # fetch, once per replica. A1 therefore targets 1 and, for an object over
      # the 16 MiB fan-out window (the default 256 MiB one), allows
      # <= replicas (FETCH_TOLERANCE defaults to REPLICAS-1 in the driver).
      PROXY_SINGLEFLIGHT_ADVISORY_LOCKS_ENABLED: "${PROXY_SINGLEFLIGHT_ADVISORY_LOCKS_ENABLED:-true}"
      ENVIRONMENT: development
      RUST_LOG: info
      HOST: 0.0.0.0
      PORT: "8080"
    healthcheck:
      test: ["CMD", "curl", "-sf", "http://localhost:8080/health"]
      interval: 5s
      timeout: 10s
      start_period: 30s
      retries: 20
    networks:
      - concurrency-e2e-network

  lb:
    image: nginx:alpine
    container_name: concurrency-e2e-lb
    depends_on:
      - backend
    volumes:
      - ./scripts/concurrency/nginx-lb.conf:/etc/nginx/nginx.conf:ro
    ports:
      - "18080:8080"
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/healthz"]
      interval: 5s
      timeout: 3s
      retries: 20
    networks:
      - concurrency-e2e-network

networks:
  concurrency-e2e-network:
    name: concurrency-e2e-network
    driver: bridge

volumes:
  concurrency_e2e_minio_data: