flipper 0.25.4 → 0.26.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a857af3f65cd468bfeeadab8c94c8ca5bf4c27cad345d93f05e5993a8f53a57d
4
- data.tar.gz: dcbe82968b60be3a46b66e0f19ddf3416c8830bd63c3926fd6ca77b45e404e02
3
+ metadata.gz: 5b6c4a5c4ef29f126e9a08cc7fc7c70be9831b179dfc55486b88b8c5da5fdf44
4
+ data.tar.gz: aab15578901fcab2b3717813aa37d3cc057554eea3d442f264f85707d94310f9
5
5
  SHA512:
6
- metadata.gz: '09914ec0d548868bf62ba0c3fe07cdaf4f773897d1416ec499c0a2f2355e6d38db96689c24d80a26b32ad2ecfbd6f380dd5da3e7cd44248c461826287252a487'
7
- data.tar.gz: 4518fa261e1f265bdf1718e52606bf0b25b1e84fa0431bbe64359797f073dc3cbb1d075b9961bfc054bab19e32e1edbcd2bdce37eb2d988853684bbbcf43c621
6
+ metadata.gz: 79989c8d6149b13b6c960878d4ac701f98f5e3da9b44a567092a4ace82350ec8ccffcd7e9c9ea2619a2a2508b6a392e99c05b14ab14b59b5a0b3a58b4f211150
7
+ data.tar.gz: 5192a0b9e1bebc39a28503861ba4eb35a05010a1841595e1282437c7d0694e3eccd02f215fbc6928b228948c6a02c8e9d26f85a5e23ae271fd0b32bc84bf6b53
data/Changelog.md CHANGED
@@ -1,3 +1,11 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## 0.26.0.rc1
6
+
7
+ * Cloud Background Polling (https://github.com/jnunemaker/flipper/pull/682)
8
+
1
9
  ## 0.25.4
2
10
 
3
11
  * Added read_only UI config option (https://github.com/jnunemaker/flipper/pull/679)
data/flipper.gemspec CHANGED
@@ -35,4 +35,6 @@ Gem::Specification.new do |gem|
35
35
  gem.require_paths = ['lib']
36
36
  gem.version = Flipper::VERSION
37
37
  gem.metadata = Flipper::METADATA
38
+
39
+ gem.add_dependency 'concurrent-ruby', '< 2'
38
40
  end
@@ -4,7 +4,7 @@ module Flipper
4
4
  include ::Flipper::Adapter
5
5
 
6
6
  # Public: The name of the adapter.
7
- attr_reader :name
7
+ attr_reader :name, :local, :remote
8
8
 
9
9
  # Public: Build a new sync instance.
10
10
  #
@@ -14,6 +14,10 @@ module Flipper
14
14
 
15
15
  HTTPS_SCHEME = "https".freeze
16
16
 
17
+ attr_reader :uri, :headers
18
+ attr_reader :basic_auth_username, :basic_auth_password
19
+ attr_reader :read_timeout, :open_timeout, :write_timeout, :max_retries, :debug_output
20
+
17
21
  def initialize(options = {})
18
22
  @uri = URI(options.fetch(:url))
19
23
  @headers = DEFAULT_HEADERS.merge(options[:headers] || {})
@@ -22,6 +26,7 @@ module Flipper
22
26
  @read_timeout = options[:read_timeout]
23
27
  @open_timeout = options[:open_timeout]
24
28
  @write_timeout = options[:write_timeout]
29
+ @max_retries = options.key?(:max_retries) ? options[:max_retries] : 0
25
30
  @debug_output = options[:debug_output]
26
31
  end
27
32
 
@@ -58,7 +63,8 @@ module Flipper
58
63
  http = Net::HTTP.new(uri.host, uri.port)
59
64
  http.read_timeout = @read_timeout if @read_timeout
60
65
  http.open_timeout = @open_timeout if @open_timeout
61
- apply_write_timeout(http)
66
+ http.max_retries = @max_retries if @max_retries
67
+ http.write_timeout = @write_timeout if @write_timeout
62
68
  http.set_debug_output(@debug_output) if @debug_output
63
69
 
64
70
  if uri.scheme == HTTPS_SCHEME
@@ -91,16 +97,6 @@ module Flipper
91
97
 
92
98
  request
93
99
  end
94
-
95
- def apply_write_timeout(http)
96
- if @write_timeout
97
- if RUBY_VERSION >= '2.6.0'
98
- http.write_timeout = @write_timeout
99
- else
100
- Kernel.warn("Warning: option :write_timeout requires Ruby version 2.6.0 or later")
101
- end
102
- end
103
- end
104
100
  end
105
101
  end
106
102
  end
@@ -10,7 +10,7 @@ module Flipper
10
10
  class Http
11
11
  include Flipper::Adapter
12
12
 
13
- attr_reader :name
13
+ attr_reader :name, :client
14
14
 
15
15
  def initialize(options = {})
16
16
  @client = Client.new(url: options.fetch(:url),
@@ -19,6 +19,8 @@ module Flipper
19
19
  basic_auth_password: options[:basic_auth_password],
20
20
  read_timeout: options[:read_timeout],
21
21
  open_timeout: options[:open_timeout],
22
+ write_timeout: options[:write_timeout],
23
+ max_retries: options[:max_retries],
22
24
  debug_output: options[:debug_output])
23
25
  @name = :http
24
26
  end
@@ -0,0 +1,123 @@
1
+ require 'logger'
2
+ require 'concurrent/atomic/read_write_lock'
3
+ require 'concurrent/utility/monotonic_time'
4
+ require 'concurrent/map'
5
+
6
+ module Flipper
7
+ module Adapters
8
+ class Poll
9
+ class Poller
10
+ PREFIX = "[flipper http async poll adapter]".freeze
11
+
12
+ attr_reader :thread, :pid, :mutex, :logger, :interval, :last_synced_at
13
+
14
+ def self.instances
15
+ @instances ||= Concurrent::Map.new
16
+ end
17
+ private_class_method :instances
18
+
19
+ def self.get(key, options = {})
20
+ instances.compute_if_absent(key) { new(options) }
21
+ end
22
+
23
+ def self.reset
24
+ instances.clear
25
+ end
26
+
27
+ def initialize(options = {})
28
+ @thread = nil
29
+ @pid = Process.pid
30
+ @mutex = Mutex.new
31
+ @adapter = Memory.new
32
+ @remote_adapter = options.fetch(:remote_adapter)
33
+ @logger = options.fetch(:logger) { Logger.new(STDOUT) }
34
+ @interval = options.fetch(:interval, 10).to_f
35
+ @lock = Concurrent::ReadWriteLock.new
36
+ @last_synced_at = Concurrent::AtomicFixnum.new(0)
37
+
38
+ if @interval < 1
39
+ warn "#{PREFIX} interval must be greater than or equal to 1 but was #{@interval}. Setting @interval to 1."
40
+ @interval = 1
41
+ end
42
+
43
+ @start_automatically = options.fetch(:start_automatically, true)
44
+
45
+ if options.fetch(:shutdown_automatically, true)
46
+ at_exit { stop }
47
+ end
48
+ end
49
+
50
+ def adapter
51
+ @lock.with_read_lock { Memory.new(@adapter.get_all.dup) }
52
+ end
53
+
54
+ def start
55
+ reset if forked?
56
+ ensure_worker_running
57
+ end
58
+
59
+ def stop
60
+ logger.debug { "#{PREFIX} Stopping worker" }
61
+ @thread&.kill
62
+ end
63
+
64
+ def run
65
+ loop do
66
+ sleep jitter
67
+ start = Concurrent.monotonic_time
68
+ begin
69
+ logger.debug { "#{PREFIX} Making a checkity checkity" }
70
+
71
+ adapter = Memory.new
72
+ adapter.import(@remote_adapter)
73
+
74
+ @lock.with_write_lock { @adapter.import(adapter) }
75
+ @last_synced_at.update { |time| Concurrent.monotonic_time }
76
+ rescue => exception
77
+ logger.debug { "#{PREFIX} Exception: #{exception.inspect}" }
78
+ end
79
+
80
+ sleep_interval = interval - (Concurrent.monotonic_time - start)
81
+ sleep sleep_interval if sleep_interval.positive?
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def jitter
88
+ rand
89
+ end
90
+
91
+ def forked?
92
+ pid != Process.pid
93
+ end
94
+
95
+ def ensure_worker_running
96
+ # Return early if thread is alive and avoid the mutex lock and unlock.
97
+ return if thread_alive?
98
+
99
+ # If another thread is starting worker thread, then return early so this
100
+ # thread can enqueue and move on with life.
101
+ return unless mutex.try_lock
102
+
103
+ begin
104
+ return if thread_alive?
105
+ @thread = Thread.new { run }
106
+ logger.debug { "#{PREFIX} Worker thread [#{@thread.object_id}] started" }
107
+ ensure
108
+ mutex.unlock
109
+ end
110
+ end
111
+
112
+ def thread_alive?
113
+ @thread && @thread.alive?
114
+ end
115
+
116
+ def reset
117
+ @pid = Process.pid
118
+ mutex.unlock if mutex.locked?
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,35 @@
1
+ require 'flipper/adapters/sync/synchronizer'
2
+
3
+ module Flipper
4
+ module Adapters
5
+ class Poll
6
+ extend Forwardable
7
+ include ::Flipper::Adapter
8
+
9
+ # Public: The name of the adapter.
10
+ attr_reader :name, :adapter, :poller
11
+
12
+ def_delegators :synced_adapter, :features, :get, :get_multi, :get_all, :add, :remove, :clear, :enable, :disable
13
+
14
+ def initialize(poller, adapter)
15
+ @name = :poll
16
+ @adapter = adapter
17
+ @poller = poller
18
+ @last_synced_at = 0
19
+ @poller.start
20
+ end
21
+
22
+ private
23
+
24
+ def synced_adapter
25
+ @poller.start
26
+ poller_last_synced_at = @poller.last_synced_at.value
27
+ if poller_last_synced_at > @last_synced_at
28
+ Flipper::Adapters::Sync::Synchronizer.new(@adapter, @poller.adapter).call
29
+ @last_synced_at = poller_last_synced_at
30
+ end
31
+ @adapter
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,3 +1,3 @@
1
1
  module Flipper
2
- VERSION = '0.25.4'.freeze
2
+ VERSION = '0.26.0.rc1'.freeze
3
3
  end
data/spec/spec_helper.rb CHANGED
@@ -22,7 +22,7 @@ Dir[FlipperRoot.join('spec/support/**/*.rb')].sort.each { |f| require f }
22
22
 
23
23
  RSpec.configure do |config|
24
24
  config.before(:example) do
25
- Flipper::Cloud::Registry.default.clear if defined?(Flipper::Cloud)
25
+ Flipper::Adapters::Poll::Poller.reset if defined?(Flipper::Adapters::Poll::Poller)
26
26
  Flipper.unregister_groups
27
27
  Flipper.configuration = nil
28
28
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flipper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.25.4
4
+ version: 0.26.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Nunemaker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-07 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2022-11-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: concurrent-ruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "<"
18
+ - !ruby/object:Gem::Version
19
+ version: '2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "<"
25
+ - !ruby/object:Gem::Version
26
+ version: '2'
13
27
  description:
14
28
  email:
15
29
  - nunemaker@gmail.com
@@ -68,6 +82,8 @@ files:
68
82
  - lib/flipper/adapters/memoizable.rb
69
83
  - lib/flipper/adapters/memory.rb
70
84
  - lib/flipper/adapters/operation_logger.rb
85
+ - lib/flipper/adapters/poll.rb
86
+ - lib/flipper/adapters/poll/poller.rb
71
87
  - lib/flipper/adapters/pstore.rb
72
88
  - lib/flipper/adapters/read_only.rb
73
89
  - lib/flipper/adapters/sync.rb
@@ -179,9 +195,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
179
195
  version: '0'
180
196
  required_rubygems_version: !ruby/object:Gem::Requirement
181
197
  requirements:
182
- - - ">="
198
+ - - ">"
183
199
  - !ruby/object:Gem::Version
184
- version: '0'
200
+ version: 1.3.1
185
201
  requirements: []
186
202
  rubygems_version: 3.3.7
187
203
  signing_key: