diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 0000000..11fd75c --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,24 @@ +# +# Github benchmark test workflow +# +name: CI Benchmark Test +on: [pull_request] +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + ruby: + - '3.2.0' + services: + redis: + image: redis + ports: ["6379:6379"] + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Run benchmark tests + run: bundle exec ruby benchmark.rb diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..8aa4d28 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,29 @@ +# +# Github ruby gem build workflow +# +name: CI Ruby Gem Build +on: [pull_request] +jobs: + release-please: + runs-on: ubuntu-latest + strategy: + matrix: + ruby: + - '3.2.0' + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Build Ruby Gem + run: gem build *.gemspec +# - name: publish gem +# run: | +# mkdir -p $HOME/.gem +# touch $HOME/.gem/credentials +# chmod 0600 $HOME/.gem/credentials +# printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials +# gem push *.gem +# env: +# GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_AUTH_TOKEN }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..658b267 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,26 @@ +# +# Github codeql analysis workflow +# +name: CI CodeQL Analysis +on: [pull_request] +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + strategy: + fail-fast: false + matrix: + language: ['ruby'] + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{matrix.language}} + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index c284281..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Ruby - -on: - push: - branches: - - master - - pull_request: - -jobs: - build: - runs-on: ubuntu-latest - name: Ruby ${{ matrix.ruby }} - strategy: - matrix: - ruby: - - '3.2.0' - - steps: - - uses: actions/checkout@v4 - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - bundler-cache: true - - name: Run the default task - run: bundle exec rake diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml new file mode 100644 index 0000000..591419b --- /dev/null +++ b/.github/workflows/rspec.yml @@ -0,0 +1,24 @@ +# +# Github rspec unit test workflow +# +name: CI RSpec Tests +on: [pull_request] +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + ruby: + - '3.2.0' + services: + redis: + image: redis + ports: ["6379:6379"] + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Run unit tests + run: bundle exec rspec diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml new file mode 100644 index 0000000..595e7ff --- /dev/null +++ b/.github/workflows/rubocop.yml @@ -0,0 +1,34 @@ +# +# Github rubocop linting workflow +# +name: CI Rubocop Linting +on: [pull_request] +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + ruby: + - '3.2.0' +# steps: +# - uses: actions/checkout@v2 +# - name: Rubocop Linter Action +# uses: andrewmcodes/rubocop-linter-action@v3.3.0 +# env: +# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Rubocop Linter Action + run: | + bundle exec rubocop \ + --parallel \ + --fail-level convention \ + --display-only-fail-level-offenses \ + --display-style-guide \ + --force-exclusion \ + --format progress \ + --format github diff --git a/.rubocop.yml b/.rubocop.yml index 872ac24..6913bf7 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,12 +1,14 @@ require: - rubocop-rake - rubocop-rspec + - rubocop-factory_bot AllCops: NewCops: enable - TargetRubyVersion: 3.1 + TargetRubyVersion: 3.2 Exclude: - 'test.rb' + - 'vendor/bundle/**/*' Metrics/MethodLength: Enabled: false diff --git a/Gemfile b/Gemfile index f625e84..843b500 100644 --- a/Gemfile +++ b/Gemfile @@ -5,12 +5,13 @@ source 'https://rubygems.org' # Specify your gem's dependencies in redis_single_file.gemspec gemspec +gem 'benchmark', '~> 0.4.0' +gem 'benchmark-ips', '~> 2.14.0' +gem 'mock_redis', '~> 0.49.0' gem 'pry', '~> 0.15.2' gem 'rake', '~> 13.0' gem 'rspec', '~> 3.0' gem 'rubocop', '~> 1.21' +gem 'rubocop-factory_bot', '~> 2.26.1' gem 'rubocop-rake', '~> 0.6.0' gem 'rubocop-rspec', '~> 3.4.0 ' -gem 'mock_redis', '~> 0.49.0' -gem 'benchmark', '~> 0.4.0' -gem 'benchmark-ips', '~> 2.14.0' diff --git a/Gemfile.lock b/Gemfile.lock index 640f08f..6774138 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -58,6 +58,8 @@ GEM unicode-display_width (>= 2.4.0, < 4.0) rubocop-ast (1.38.0) parser (>= 3.3.1.0) + rubocop-factory_bot (2.26.1) + rubocop (~> 1.61) rubocop-rake (0.6.0) rubocop (~> 1.0) rubocop-rspec (3.4.0) @@ -80,6 +82,7 @@ DEPENDENCIES redis-single-file! rspec (~> 3.0) rubocop (~> 1.21) + rubocop-factory_bot (~> 2.26.1) rubocop-rake (~> 0.6.0) rubocop-rspec (~> 3.4.0) diff --git a/benchmark.rb b/benchmark.rb index 1e5d1e0..02b5571 100644 --- a/benchmark.rb +++ b/benchmark.rb @@ -3,8 +3,8 @@ require 'benchmark/ips' require 'redis_single_file' -scenario_1_semaphore = RedisSingleFile.new(name: :scenario_1) -scenario_2_semaphore = RedisSingleFile.new(name: :scenario_2) +scenario_1_semaphore = RedisSingleFile.new(name: :scenario1) +scenario_2_semaphore = RedisSingleFile.new(name: :scenario2) Benchmark.ips do |x| x.report('synchronize') do @@ -18,20 +18,18 @@ x.report('threaded (10x)') do threads = 10.times.map do Thread.new do - scenario_3_semaphore = RedisSingleFile.new(name: :scenario_3) + scenario_3_semaphore = RedisSingleFile.new(name: :scenario3) scenario_3_semaphore.synchronize { nil } end end - while threads.any?(&:alive?) do - threads.each { _1.join(0.5) } - end + threads.each { _1.join(0.05) } while threads.any?(&:alive?) end x.report('forked (10x)') do 10.times.each do fork do - scenario_4_semaphore = RedisSingleFile.new(name: :scenario_4) + scenario_4_semaphore = RedisSingleFile.new(name: :scenario4) scenario_4_semaphore.synchronize { nil } end end diff --git a/lib/redis_single_file/semaphore.rb b/lib/redis_single_file/semaphore.rb index 1e3f230..4f55aa5 100644 --- a/lib/redis_single_file/semaphore.rb +++ b/lib/redis_single_file/semaphore.rb @@ -80,8 +80,8 @@ def initialize( # @param timeout [Integer] seconds for client to wait in queue # @yieldreturn [...] response from synchronized block execution # @return [nil] redis blpop timeout - def synchronize(timeout: 0, &blk) - synchronize!(timeout:, &blk) + def synchronize(timeout: 0, &) + synchronize!(timeout:, &) rescue QueueTimeout => _e nil end @@ -100,8 +100,8 @@ def synchronize!(timeout: 0) raise QueueTimeout unless redis.blpop(queue_key, timeout:) redis.multi do - redis.persist(mutex_key) - redis.persist(queue_key) + redis.persist(mutex_key) # unexpire during execution + redis.persist(queue_key) # unexpire during execution end end diff --git a/redis-single-file.gemspec b/redis-single-file.gemspec index b949df7..ae672f1 100644 --- a/redis-single-file.gemspec +++ b/redis-single-file.gemspec @@ -10,11 +10,11 @@ Gem::Specification.new do |spec| spec.summary = 'Distributed semaphore implementation with redis.' spec.description = 'Synchronize execution across numerous instances.' - spec.homepage = 'https://coming.com/soon' + spec.homepage = 'https://github.com/lifeBCE/redis-single-file' spec.license = 'MIT' - spec.required_ruby_version = '>= 3.1.0' + spec.required_ruby_version = '>= 3.2.0' - spec.metadata['allowed_push_host'] = "TODO: Set to your gem server 'https://example.com'" + # spec.metadata['allowed_push_host'] = "TODO: Set to your gem server 'https://example.com'" spec.metadata['homepage_uri'] = spec.homepage # spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."