---
# @see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
#
# Services feature of GitHub Actions isn't fit for our purposes for testing.
# We cannot overwrite arguments of ENTRYPOINT.
# @see https://docs.docker.com/engine/reference/commandline/create/#options
#
# @see https://github.community/t/how-to-trigger-an-action-on-push-or-pull-request-but-not-both/16662
name: Test
permissions:
  contents: read
on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master
  schedule:
    - cron: '0 9,21 * * *'
defaults:
  run:
    shell: bash
jobs:
  main:
    name: Main
    if: github.event_name != 'schedule' || github.repository == 'redis-rb/redis-cluster-client'
    timeout-minutes: 15
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      max-parallel: 8
      matrix:
        include:
          - {redis: '8', ruby: '4.0'}
          - {redis: '8', ruby: '4.0', compose: compose.ssl.yaml}
          - {redis: '8', ruby: '4.0', driver: 'hiredis'}
          - {redis: '8', ruby: '4.0', driver: 'hiredis', compose: compose.ssl.yaml}
          - {redis: '8', ruby: '4.0', compose: compose.auth.yaml}
          - {redis: '8', ruby: '4.0', compose: compose.replica.yaml, replica: '2'}
          - {redis: '9', ruby: '4.0', compose: compose.valkey.yaml, replica: '2'}
          - {redis: '8', ruby: '4.0', compose: compose.valkey.yaml, replica: '2'}
          - {redis: '7', ruby: '3.4'}
          - {redis: '6', ruby: '3.3'}
          - {redis: '5', ruby: '2.7'}
          - {ruby: 'jruby'}
          - {ruby: 'truffleruby'}
          - {task: test_cluster_down}
          - {task: test_cluster_broken, restart: 'no', startup: '6'}
          - {task: test_cluster_state, pattern: 'PrimaryOnly', compose: compose.valkey.yaml, redis: '9', replica: '2', startup: '9'}
          - {task: test_cluster_state, pattern: 'Pooled', compose: compose.valkey.yaml, redis: '9', replica: '2', startup: '9'}
          - {task: test_cluster_state, pattern: 'ScaleReadRandom', compose: compose.valkey.yaml, redis: '9', replica: '2', startup: '9'}
          - {task: test_cluster_state, pattern: 'ScaleReadRandomWithPrimary', compose: compose.valkey.yaml, redis: '9', replica: '2', startup: '9'}
          - {task: test_cluster_state, pattern: 'ScaleReadLatency', compose: compose.valkey.yaml, redis: '9', replica: '2', startup: '9'}
          - {task: test_cluster_scale, pattern: 'Single', compose: compose.scale.yaml, startup: '8'}
          - {task: test_cluster_scale, pattern: 'Pipeline', compose: compose.scale.yaml, startup: '8'}
          - {task: test_cluster_scale, pattern: 'Transaction', compose: compose.scale.yaml, startup: '8'}
          - {task: test_cluster_scale, pattern: 'PubSub', compose: compose.scale.yaml, startup: '8'}
    env:
      REDIS_VERSION: ${{ matrix.redis || '8' }}
      DOCKER_COMPOSE_FILE: ${{ matrix.compose || 'compose.yaml' }}
      REDIS_CONNECTION_DRIVER: ${{ matrix.driver || 'ruby' }}
      REDIS_REPLICA_SIZE: ${{ matrix.replica || '1' }}
      RESTART_POLICY: ${{ matrix.restart || 'always' }}
      REDIS_CLIENT_MAX_STARTUP_SAMPLE: ${{ matrix.startup || '3' }}
      TEST_CLASS_PATTERN: ${{ matrix.pattern || '' }}
    steps:
      - name: Check out code
        uses: actions/checkout@v4
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: ${{ matrix.ruby || '4.0' }}
          bundler-cache: true
      - name: Pull Docker images
        run: docker compose --progress quiet -f $DOCKER_COMPOSE_FILE pull
      - name: Run containers
        run: docker compose --progress quiet -f $DOCKER_COMPOSE_FILE up -d
      - name: Wait for Redis cluster to be ready
        run: bundle exec rake wait
      - name: Print containers
        run: docker compose -f $DOCKER_COMPOSE_FILE ps
      - name: Run minitest
        run: bundle exec rake ${{ matrix.task || 'test' }}
      - name: Stop containers
        run: docker compose --progress quiet -f $DOCKER_COMPOSE_FILE down || true
  lint:
    name: Lint
    if: github.event_name != 'schedule' || github.repository == 'redis-rb/redis-cluster-client'
    timeout-minutes: 5
    runs-on: ubuntu-latest
    steps:
      - name: Check out code
        uses: actions/checkout@v4
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '4.0'
          bundler-cache: true
      - name: Run rubocop
        run: bundle exec rubocop
  nat-ted-env:
    if: github.event_name == 'schedule' && github.repository == 'redis-rb/redis-cluster-client'
    name: NAT-ted Environments
    timeout-minutes: 5
    runs-on: ubuntu-latest
    env:
      REDIS_VERSION: '8'
      DOCKER_COMPOSE_FILE: 'compose.nat.yaml'
    steps:
      - name: Check out code
        uses: actions/checkout@v4
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '4.0'
          bundler-cache: true
      - name: Get IP address of host
        run: |
          host_ip_addr=$(ip a | grep eth0 | grep inet | awk '{print $2}' | cut -d'/' -f1)
          echo "HOST_IP_ADDR=$host_ip_addr" >> $GITHUB_ENV
      - name: Pull Docker images
        run: docker compose --progress quiet -f $DOCKER_COMPOSE_FILE pull
      - name: Run containers
        run: docker compose --progress quiet -f $DOCKER_COMPOSE_FILE up --wait --wait-timeout 30
        env:
          HOST_ADDR: ${{ env.HOST_IP_ADDR }}
      - name: Print containers
        run: docker compose -f $DOCKER_COMPOSE_FILE ps
      - name: Build cluster
        run: bundle exec rake "build_cluster[$HOST_ADDR]"
        env:
          HOST_ADDR: ${{ env.HOST_IP_ADDR }}
          DEBUG: "1"
      - name: Run minitest
        run: bundle exec rake test
      - name: Stop containers
        run: docker compose --progress quiet -f $DOCKER_COMPOSE_FILE down || true
  ips:
    if: github.event_name == 'schedule' && github.repository == 'redis-rb/redis-cluster-client'
    name: IPS
    timeout-minutes: 10
    runs-on: ubuntu-latest
    env:
      REDIS_VERSION: '8'
      DOCKER_COMPOSE_FILE: 'compose.latency.yaml'
      REDIS_REPLICA_SIZE: '2'
      REDIS_CLIENT_MAX_THREADS: '10'
      DELAY_TIME: '0ms'
    steps:
      - name: Check out code
        uses: actions/checkout@v4
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '4.0'
          bundler-cache: true
      - name: Pull Docker images
        run: docker compose --progress quiet -f $DOCKER_COMPOSE_FILE pull
      - name: Run containers
        run: docker compose --progress quiet -f $DOCKER_COMPOSE_FILE up -d
      - name: Wait for Redis cluster to be ready
        run: bundle exec rake wait
      - name: Print containers
        run: docker compose -f $DOCKER_COMPOSE_FILE ps
      - name: Print cpu info
        run: grep 'model name' /proc/cpuinfo
      - name: Run iteration per second
        run: bundle exec rake ips
      - name: Stop containers
        run: docker compose --progress quiet -f $DOCKER_COMPOSE_FILE down || true
  profiling:
    if: github.event_name == 'schedule' && github.repository == 'redis-rb/redis-cluster-client'
    name: Profiling
    timeout-minutes: 5
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        mode:
          - single
          - excessive_pipelining
          - pipelining_in_moderation
          - original_mget
          - emulated_mget
    env:
      REDIS_VERSION: '8'
      DOCKER_COMPOSE_FILE: 'compose.yaml'
    steps:
      - name: Check out code
        uses: actions/checkout@v4
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '4.0'
          bundler-cache: true
      - name: Pull Docker images
        run: docker compose --progress quiet -f $DOCKER_COMPOSE_FILE pull
      - name: Run containers
        run: docker compose --progress quiet -f $DOCKER_COMPOSE_FILE up -d
      - name: Wait for Redis cluster to be ready
        run: bundle exec rake wait
      - name: Print containers
        run: docker compose -f $DOCKER_COMPOSE_FILE ps
      - name: Run profiler
        run: bundle exec rake prof
        env:
          PROFILE_MODE: ${{ matrix.mode }}
      - name: Stop containers
        run: docker compose --progress quiet -f $DOCKER_COMPOSE_FILE down || true
  massive:
    if: github.event_name == 'schedule' && github.repository == 'redis-rb/redis-cluster-client'
    name: Massive Cluster
    timeout-minutes: 10
    runs-on: ubuntu-latest
    env:
      REDIS_VERSION: '8'
      DOCKER_COMPOSE_FILE: 'compose.massive.yaml'
      REDIS_SHARD_SIZE: '10'
      REDIS_REPLICA_SIZE: '2'
      REDIS_CLIENT_MAX_STARTUP_SAMPLE: '5'
    steps:
      - name: Check out code
        uses: actions/checkout@v4
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '4.0'
          bundler-cache: true
      - name: Print user limits
        run: ulimit -a
      - name: Print kernel params
        run: |
          sysctl fs.file-max
          sysctl vm.swappiness
          sysctl vm.overcommit_memory
          sysctl net.ipv4.tcp_sack
          sysctl net.ipv4.tcp_timestamps
          sysctl net.ipv4.tcp_window_scaling
          sysctl net.ipv4.tcp_congestion_control
          sysctl net.ipv4.tcp_syncookies
          sysctl net.ipv4.tcp_tw_reuse
          sysctl net.ipv4.tcp_max_syn_backlog
          sysctl net.core.somaxconn
          sysctl net.core.rmem_max
          sysctl net.core.wmem_max
      - name: Tune kernel params for redis
        run: |
          # https://developer.redis.com/operate/redis-at-scale/talking-to-redis/initial-tuning/
          sudo sysctl -w vm.overcommit_memory=1
          sudo sysctl -w net.ipv4.tcp_tw_reuse=1           # reuse sockets quickly
          sudo sysctl -w net.ipv4.tcp_max_syn_backlog=1024 # backlog setting
          sudo sysctl -w net.core.somaxconn=1024           # up the number of connections per port
      - name: Pull Docker images
        run: docker compose --progress quiet -f $DOCKER_COMPOSE_FILE pull
      - name: Run containers
        run: docker compose --progress quiet -f $DOCKER_COMPOSE_FILE up -d
      - name: Print memory info
        run: free -w
      - name: Wait for Redis cluster to be ready
        run: bundle exec rake wait
        env:
          DEBUG: '1'
      - name: Print containers
        run: docker compose -f $DOCKER_COMPOSE_FILE ps
      - name: Run profiler
        run: bundle exec rake prof
        env:
          PROFILE_MODE: pipelining_in_moderation
      - name: Stop containers
        run: docker compose --progress quiet -f $DOCKER_COMPOSE_FILE down || true
