service_skeleton 2.0.2 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +28 -13
- data/.rubocop.yml +1 -1
- data/README.md +1 -1
- data/lib/service_skeleton/hurriable_timer.rb +61 -0
- data/lib/service_skeleton/hurriable_timer_sequence.rb +35 -0
- data/lib/service_skeleton.rb +2 -0
- data/service_skeleton.gemspec +2 -2
- data/ultravisor/lib/ultravisor/child/call_receiver.rb +1 -0
- data/ultravisor/lib/ultravisor/child/cast_receiver.rb +1 -0
- data/ultravisor/spec/spec_helper.rb +4 -2
- data/ultravisor/spec/ultravisor/child/cast_spec.rb +24 -0
- data/ultravisor/spec/ultravisor/child/restart_delay_spec.rb +1 -1
- data/ultravisor/spec/ultravisor/child/shutdown_spec.rb +1 -1
- data/ultravisor/spec/ultravisor/child/unsafe_instance_spec.rb +1 -1
- data/ultravisor/spec/ultravisor/run_spec.rb +3 -3
- metadata +10 -8
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: f06ab46da1ee2c8366933e34efe02686cfca1758cff0667fb05b6ef5a42e9db6
         | 
| 4 | 
            +
              data.tar.gz: 17cc7ba27c8cd63f6e74831e69da68c860803af15e77eb6f4a52686754107138
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: c3137b6133e59bd2c9aa990bd85216b6acdf09a1d6174ae0fa06a1c2ff55e898f266f131a69457dd39f363fcdef35d67d0f9fd1e9255f6a8e9b4ebdd2f4a5ed9
         | 
| 7 | 
            +
              data.tar.gz: dbba001733bcf0717673335d1ff52d6eb33b0a88b602c7f3be2d378addb37c0da585fba5c15d6ddff106f0dd69663935d541583ea521376b8136711eef53f3b8
         | 
    
        data/.github/workflows/ci.yml
    CHANGED
    
    | @@ -4,23 +4,41 @@ on: | |
| 4 4 | 
             
              pull_request:
         | 
| 5 5 | 
             
              push:
         | 
| 6 6 | 
             
                branches:
         | 
| 7 | 
            -
                  - master
         | 
| 8 7 | 
             
                  - main
         | 
| 9 8 |  | 
| 10 9 | 
             
            jobs:
         | 
| 10 | 
            +
              lint:
         | 
| 11 | 
            +
                runs-on: ubuntu-latest
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                steps:
         | 
| 14 | 
            +
                  - uses: actions/checkout@v4
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  - name: Setup ruby
         | 
| 17 | 
            +
                    uses: ruby/setup-ruby@v1
         | 
| 18 | 
            +
                    with:
         | 
| 19 | 
            +
                      ruby-version: "3.2"
         | 
| 20 | 
            +
                      bundler-cache: true
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  - name: Lint
         | 
| 23 | 
            +
                    run: bundle exec rubocop
         | 
| 24 | 
            +
             | 
| 11 25 | 
             
              build:
         | 
| 12 26 | 
             
                runs-on: ubuntu-latest
         | 
| 13 27 |  | 
| 14 28 | 
             
                strategy:
         | 
| 29 | 
            +
                  fail-fast: false
         | 
| 30 | 
            +
             | 
| 15 31 | 
             
                  matrix:
         | 
| 16 32 | 
             
                    ruby:
         | 
| 17 | 
            -
                      - 2.5
         | 
| 18 | 
            -
                      - 2.6
         | 
| 19 | 
            -
                      - 2.7
         | 
| 20 | 
            -
                      - 3.0
         | 
| 33 | 
            +
                      - "2.5"
         | 
| 34 | 
            +
                      - "2.6"
         | 
| 35 | 
            +
                      - "2.7"
         | 
| 36 | 
            +
                      - "3.0"
         | 
| 37 | 
            +
                      - "3.1"
         | 
| 38 | 
            +
                      - "3.2"
         | 
| 21 39 |  | 
| 22 40 | 
             
                steps:
         | 
| 23 | 
            -
                  - uses: actions/checkout@ | 
| 41 | 
            +
                  - uses: actions/checkout@v4
         | 
| 24 42 |  | 
| 25 43 | 
             
                  - name: Setup ruby
         | 
| 26 44 | 
             
                    uses: ruby/setup-ruby@v1
         | 
| @@ -28,22 +46,19 @@ jobs: | |
| 28 46 | 
             
                      ruby-version: ${{ matrix.ruby }}
         | 
| 29 47 | 
             
                      bundler-cache: true
         | 
| 30 48 |  | 
| 31 | 
            -
                  - name: Lint
         | 
| 32 | 
            -
                    run: bundle exec rubocop
         | 
| 33 | 
            -
             | 
| 34 49 | 
             
                  - name: Tests
         | 
| 35 50 | 
             
                    run: bundle exec rake test
         | 
| 36 51 |  | 
| 37 52 | 
             
              publish:
         | 
| 38 | 
            -
                if: github.event_name == 'push' &&  | 
| 39 | 
            -
                needs: build
         | 
| 53 | 
            +
                if: github.event_name == 'push' && github.ref == 'refs/heads/main'
         | 
| 54 | 
            +
                needs: [lint, build]
         | 
| 40 55 | 
             
                runs-on: ubuntu-latest
         | 
| 41 56 |  | 
| 42 57 | 
             
                steps:
         | 
| 43 | 
            -
                  - uses: actions/checkout@ | 
| 58 | 
            +
                  - uses: actions/checkout@v4
         | 
| 44 59 |  | 
| 45 60 | 
             
                  - name: Release Gem
         | 
| 46 | 
            -
                    uses: discourse/publish-rubygems-action@ | 
| 61 | 
            +
                    uses: discourse/publish-rubygems-action@v3
         | 
| 47 62 | 
             
                    env:
         | 
| 48 63 | 
             
                      RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
         | 
| 49 64 | 
             
                      GIT_EMAIL: team@discourse.org
         | 
    
        data/.rubocop.yml
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | @@ -268,7 +268,7 @@ default for a config variable, like so: | |
| 268 268 | 
             
                class GenericHelloService
         | 
| 269 269 | 
             
                  include ServiceSkeleton
         | 
| 270 270 |  | 
| 271 | 
            -
                  string :RECIPIENT, match: /\ | 
| 271 | 
            +
                  string :RECIPIENT, match: /\A\w+\z/, default: "Anonymous Coward"
         | 
| 272 272 |  | 
| 273 273 | 
             
                  # ...
         | 
| 274 274 |  | 
| @@ -0,0 +1,61 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # A mechanism for waiting until a timer expires or until another thread signals
         | 
| 4 | 
            +
            # readiness.
         | 
| 5 | 
            +
            class ServiceSkeleton::HurriableTimer
         | 
| 6 | 
            +
              def initialize(timeout)
         | 
| 7 | 
            +
                @mutex = Mutex.new
         | 
| 8 | 
            +
                @condition = ConditionVariable.new
         | 
| 9 | 
            +
                @end_time = now + timeout
         | 
| 10 | 
            +
                @hurried = false
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              # Wait for the timer to elapse
         | 
| 14 | 
            +
              #
         | 
| 15 | 
            +
              # Any number of threads can wait on the same HurriableTimer
         | 
| 16 | 
            +
              def wait(t = nil)
         | 
| 17 | 
            +
                end_time =
         | 
| 18 | 
            +
                  if t
         | 
| 19 | 
            +
                    [@end_time, now + t].min
         | 
| 20 | 
            +
                  else
         | 
| 21 | 
            +
                    @end_time
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                @mutex.synchronize {
         | 
| 25 | 
            +
                  while true
         | 
| 26 | 
            +
                    remaining = end_time - now
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    if remaining < 0 || @hurried
         | 
| 29 | 
            +
                      break
         | 
| 30 | 
            +
                    else
         | 
| 31 | 
            +
                      @condition.wait(@mutex, remaining)
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
                }
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                nil
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              # Cause the timer to trigger early if it hasn't already expired
         | 
| 40 | 
            +
              #
         | 
| 41 | 
            +
              # This method is idempotent
         | 
| 42 | 
            +
              def hurry!
         | 
| 43 | 
            +
                @mutex.synchronize {
         | 
| 44 | 
            +
                  @hurried = true
         | 
| 45 | 
            +
                  @condition.broadcast
         | 
| 46 | 
            +
                }
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                nil
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
              def expired?
         | 
| 52 | 
            +
                @hurried || @end_time - now < 0
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              private
         | 
| 56 | 
            +
             | 
| 57 | 
            +
              def now
         | 
| 58 | 
            +
                # Using this instead of Time.now, because it isn't affected by NTP updates
         | 
| 59 | 
            +
                Process.clock_gettime(Process::CLOCK_MONOTONIC_RAW)
         | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
            end
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # HurribleTimerSequence is a resettable version of HurriableTimer, designed for
         | 
| 4 | 
            +
            # cases where some action needs to happen at at least some frequency, but may
         | 
| 5 | 
            +
            # happen more often when other threads trigger the process early.
         | 
| 6 | 
            +
            #
         | 
| 7 | 
            +
            # It would have been possible to implement this without requiring allocation on
         | 
| 8 | 
            +
            # reset, by reusing the mutex and condition variable in the normal timer, but
         | 
| 9 | 
            +
            # this version is more obviously correct.
         | 
| 10 | 
            +
            class ServiceSkeleton::HurriableTimerSequence
         | 
| 11 | 
            +
              def initialize(timeout)
         | 
| 12 | 
            +
                @mutex = Mutex.new
         | 
| 13 | 
            +
                @timeout = timeout
         | 
| 14 | 
            +
                @latest = ServiceSkeleton::HurriableTimer.new(@timeout)
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              def reset!
         | 
| 18 | 
            +
                @mutex.synchronize {
         | 
| 19 | 
            +
                  @latest.hurry!
         | 
| 20 | 
            +
                  @latest = ServiceSkeleton::HurriableTimer.new(@timeout)
         | 
| 21 | 
            +
                }
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              def wait(t = nil)
         | 
| 25 | 
            +
                @mutex.synchronize { @latest }.wait(t)
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              def hurry!
         | 
| 29 | 
            +
                @mutex.synchronize { @latest }.hurry!
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              def expired?
         | 
| 33 | 
            +
                @mutex.synchronize { @latest }.expired?
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
    
        data/lib/service_skeleton.rb
    CHANGED
    
    | @@ -3,6 +3,8 @@ | |
| 3 3 | 
             
            require_relative "service_skeleton/config_class"
         | 
| 4 4 | 
             
            require_relative "service_skeleton/config_variables"
         | 
| 5 5 | 
             
            require_relative "service_skeleton/generator"
         | 
| 6 | 
            +
            require_relative "service_skeleton/hurriable_timer"
         | 
| 7 | 
            +
            require_relative "service_skeleton/hurriable_timer_sequence"
         | 
| 6 8 | 
             
            require_relative "service_skeleton/logging_helpers"
         | 
| 7 9 | 
             
            require_relative "service_skeleton/metrics_methods"
         | 
| 8 10 | 
             
            require_relative "service_skeleton/service_name"
         | 
    
        data/service_skeleton.gemspec
    CHANGED
    
    | @@ -3,7 +3,7 @@ | |
| 3 3 | 
             
            Gem::Specification.new do |s|
         | 
| 4 4 | 
             
              s.name = "service_skeleton"
         | 
| 5 5 |  | 
| 6 | 
            -
              s.version = '2.0 | 
| 6 | 
            +
              s.version = '2.2.0'
         | 
| 7 7 |  | 
| 8 8 | 
             
              s.platform = Gem::Platform::RUBY
         | 
| 9 9 |  | 
| @@ -39,7 +39,7 @@ Gem::Specification.new do |s| | |
| 39 39 | 
             
              s.add_development_dependency 'rake'
         | 
| 40 40 | 
             
              s.add_development_dependency 'redcarpet'
         | 
| 41 41 | 
             
              s.add_development_dependency 'rspec'
         | 
| 42 | 
            -
              s.add_development_dependency 'rubocop-discourse', '~>  | 
| 42 | 
            +
              s.add_development_dependency 'rubocop-discourse', '~> 3.4.1'
         | 
| 43 43 | 
             
              s.add_development_dependency 'simplecov'
         | 
| 44 44 | 
             
              s.add_development_dependency 'yard'
         | 
| 45 45 | 
             
            end
         | 
| @@ -106,6 +106,30 @@ describe Ultravisor::Child do | |
| 106 106 |  | 
| 107 107 | 
             
                    expect { child.cast.to_s }.to_not raise_error
         | 
| 108 108 | 
             
                  end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                  context "with keyword arguments" do
         | 
| 111 | 
            +
                    class Child
         | 
| 112 | 
            +
                      def run
         | 
| 113 | 
            +
                      end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                      def process
         | 
| 116 | 
            +
                        process_castcall
         | 
| 117 | 
            +
                      end
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                      def kwarg(message:)
         | 
| 120 | 
            +
                        raise "#{message} was passed"
         | 
| 121 | 
            +
                      end
         | 
| 122 | 
            +
                    end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                    let(:args) do
         | 
| 125 | 
            +
                      { id: :child, klass: Child, method: :run, enable_castcall: true, access: :unsafe }
         | 
| 126 | 
            +
                    end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                    it "forwards them" do
         | 
| 129 | 
            +
                      child.cast.kwarg(message: "hey")
         | 
| 130 | 
            +
                      expect { child.unsafe_instance.process }.to raise_error("hey was passed")
         | 
| 131 | 
            +
                    end
         | 
| 132 | 
            +
                  end
         | 
| 109 133 | 
             
                end
         | 
| 110 134 | 
             
              end
         | 
| 111 135 | 
             
            end
         | 
| @@ -10,7 +10,7 @@ describe Ultravisor::Child do | |
| 10 10 | 
             
              let(:mock_class) { Class.new.tap { |k| k.class_eval { def run; end } } }
         | 
| 11 11 |  | 
| 12 12 | 
             
              describe "#restart_delay" do
         | 
| 13 | 
            -
                context " | 
| 13 | 
            +
                context "with default args" do
         | 
| 14 14 | 
             
                  let(:args) { base_args }
         | 
| 15 15 |  | 
| 16 16 | 
             
                  it "returns the default delay" do
         | 
| @@ -77,7 +77,7 @@ describe Ultravisor::Child do | |
| 77 77 | 
             
                      expect(@thread).to have_received(:join).with(0.05)
         | 
| 78 78 | 
             
                    end
         | 
| 79 79 |  | 
| 80 | 
            -
                    context "the worker doesn't finish quickly enough" do
         | 
| 80 | 
            +
                    context "when the worker doesn't finish quickly enough" do
         | 
| 81 81 | 
             
                      before(:each) do
         | 
| 82 82 | 
             
                        allow(mock_instance).to receive(:run) { sleep 15 }
         | 
| 83 83 | 
             
                      end
         | 
| @@ -10,7 +10,7 @@ describe Ultravisor::Child do | |
| 10 10 | 
             
              let(:child) { Ultravisor::Child.new(**args) }
         | 
| 11 11 |  | 
| 12 12 | 
             
              describe "#unsafe_instance" do
         | 
| 13 | 
            -
                context " | 
| 13 | 
            +
                context "with default args" do
         | 
| 14 14 | 
             
                  it "explodes" do
         | 
| 15 15 | 
             
                    expect { child.unsafe_instance }.to raise_error(Ultravisor::ThreadSafetyError)
         | 
| 16 16 | 
             
                  end
         | 
| @@ -69,13 +69,13 @@ describe Ultravisor do | |
| 69 69 | 
             
                    ultravisor.run
         | 
| 70 70 | 
             
                  end
         | 
| 71 71 |  | 
| 72 | 
            -
                  context " | 
| 72 | 
            +
                  context "when the child terminates" do
         | 
| 73 73 | 
             
                    before(:each) do
         | 
| 74 74 | 
             
                      allow(ultravisor.instance_variable_get(:@queue)).to receive(:pop).and_return(child, :shutdown)
         | 
| 75 75 | 
             
                      allow(ultravisor).to receive(:sleep)
         | 
| 76 76 | 
             
                    end
         | 
| 77 77 |  | 
| 78 | 
            -
                    context " | 
| 78 | 
            +
                    context "when in the limits of its restart policy" do
         | 
| 79 79 | 
             
                      it "spawns the child again" do
         | 
| 80 80 | 
             
                        expect(child).to receive(:spawn).exactly(:twice)
         | 
| 81 81 |  | 
| @@ -89,7 +89,7 @@ describe Ultravisor do | |
| 89 89 | 
             
                      end
         | 
| 90 90 | 
             
                    end
         | 
| 91 91 |  | 
| 92 | 
            -
                    context "too often for its restart policy" do
         | 
| 92 | 
            +
                    context "when it terminates too often for its restart policy" do
         | 
| 93 93 | 
             
                      before(:each) do
         | 
| 94 94 | 
             
                        allow(child).to receive(:restart?).and_raise(Ultravisor::BlownRestartPolicyError)
         | 
| 95 95 | 
             
                        allow(logger).to receive(:error)
         | 
    
        metadata
    CHANGED
    
    | @@ -1,15 +1,15 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: service_skeleton
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 2.0 | 
| 4 | 
            +
              version: 2.2.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Matt Palmer
         | 
| 8 8 | 
             
            - Sam Saffron
         | 
| 9 | 
            -
            autorequire: | 
| 9 | 
            +
            autorequire:
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date:  | 
| 12 | 
            +
            date: 2023-12-04 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: frankenstein
         | 
| @@ -199,14 +199,14 @@ dependencies: | |
| 199 199 | 
             
                requirements:
         | 
| 200 200 | 
             
                - - "~>"
         | 
| 201 201 | 
             
                  - !ruby/object:Gem::Version
         | 
| 202 | 
            -
                    version:  | 
| 202 | 
            +
                    version: 3.4.1
         | 
| 203 203 | 
             
              type: :development
         | 
| 204 204 | 
             
              prerelease: false
         | 
| 205 205 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 206 206 | 
             
                requirements:
         | 
| 207 207 | 
             
                - - "~>"
         | 
| 208 208 | 
             
                  - !ruby/object:Gem::Version
         | 
| 209 | 
            -
                    version:  | 
| 209 | 
            +
                    version: 3.4.1
         | 
| 210 210 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 211 211 | 
             
              name: simplecov
         | 
| 212 212 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -275,6 +275,8 @@ files: | |
| 275 275 | 
             
            - lib/service_skeleton/error.rb
         | 
| 276 276 | 
             
            - lib/service_skeleton/filtering_logger.rb
         | 
| 277 277 | 
             
            - lib/service_skeleton/generator.rb
         | 
| 278 | 
            +
            - lib/service_skeleton/hurriable_timer.rb
         | 
| 279 | 
            +
            - lib/service_skeleton/hurriable_timer_sequence.rb
         | 
| 278 280 | 
             
            - lib/service_skeleton/logging_helpers.rb
         | 
| 279 281 | 
             
            - lib/service_skeleton/metric_method_name.rb
         | 
| 280 282 | 
             
            - lib/service_skeleton/metrics_methods.rb
         | 
| @@ -319,7 +321,7 @@ files: | |
| 319 321 | 
             
            homepage: https://github.com/discourse/service_skeleton
         | 
| 320 322 | 
             
            licenses: []
         | 
| 321 323 | 
             
            metadata: {}
         | 
| 322 | 
            -
            post_install_message: | 
| 324 | 
            +
            post_install_message:
         | 
| 323 325 | 
             
            rdoc_options: []
         | 
| 324 326 | 
             
            require_paths:
         | 
| 325 327 | 
             
            - lib
         | 
| @@ -334,8 +336,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 334 336 | 
             
                - !ruby/object:Gem::Version
         | 
| 335 337 | 
             
                  version: '0'
         | 
| 336 338 | 
             
            requirements: []
         | 
| 337 | 
            -
            rubygems_version: 3. | 
| 338 | 
            -
            signing_key: | 
| 339 | 
            +
            rubygems_version: 3.4.10
         | 
| 340 | 
            +
            signing_key:
         | 
| 339 341 | 
             
            specification_version: 4
         | 
| 340 342 | 
             
            summary: The bare bones of a service
         | 
| 341 343 | 
             
            test_files: []
         |