|
5 | 5 | require "thread" |
6 | 6 |
|
7 | 7 | module LaunchDarkly |
8 | | - # @api private |
9 | | - class PollingProcessor |
10 | | - def initialize(config, requestor) |
11 | | - @config = config |
12 | | - @requestor = requestor |
13 | | - @initialized = Concurrent::AtomicBoolean.new(false) |
14 | | - @started = Concurrent::AtomicBoolean.new(false) |
15 | | - @ready = Concurrent::Event.new |
16 | | - @task = Impl::RepeatingTask.new(@config.poll_interval, 0, -> { self.poll }, @config.logger, 'LD/PollingDataSource') |
17 | | - end |
| 8 | + module Impl |
| 9 | + module DataSource |
| 10 | + # @api private |
| 11 | + class PollingProcessor |
| 12 | + def initialize(config, requestor) |
| 13 | + @config = config |
| 14 | + @requestor = requestor |
| 15 | + @initialized = Concurrent::AtomicBoolean.new(false) |
| 16 | + @started = Concurrent::AtomicBoolean.new(false) |
| 17 | + @ready = Concurrent::Event.new |
| 18 | + @task = Impl::RepeatingTask.new(@config.poll_interval, 0, -> { self.poll }, @config.logger, 'LD/PollingDataSource') |
| 19 | + end |
18 | 20 |
|
19 | | - def initialized? |
20 | | - @initialized.value |
21 | | - end |
| 21 | + def initialized? |
| 22 | + @initialized.value |
| 23 | + end |
22 | 24 |
|
23 | | - def start |
24 | | - return @ready unless @started.make_true |
25 | | - @config.logger.info { "[LDClient] Initializing polling connection" } |
26 | | - @task.start |
27 | | - @ready |
28 | | - end |
| 25 | + def start |
| 26 | + return @ready unless @started.make_true |
| 27 | + @config.logger.info { "[LDClient] Initializing polling connection" } |
| 28 | + @task.start |
| 29 | + @ready |
| 30 | + end |
29 | 31 |
|
30 | | - def stop |
31 | | - stop_with_error_info |
32 | | - end |
| 32 | + def stop |
| 33 | + stop_with_error_info |
| 34 | + end |
33 | 35 |
|
34 | | - def poll |
35 | | - begin |
36 | | - all_data = @requestor.request_all_data |
37 | | - if all_data |
38 | | - update_sink_or_data_store.init(all_data) |
39 | | - if @initialized.make_true |
40 | | - @config.logger.info { "[LDClient] Polling connection initialized" } |
41 | | - @ready.set |
| 36 | + def poll |
| 37 | + begin |
| 38 | + all_data = @requestor.request_all_data |
| 39 | + if all_data |
| 40 | + update_sink_or_data_store.init(all_data) |
| 41 | + if @initialized.make_true |
| 42 | + @config.logger.info { "[LDClient] Polling connection initialized" } |
| 43 | + @ready.set |
| 44 | + end |
| 45 | + end |
| 46 | + @config.data_source_update_sink&.update_status(LaunchDarkly::Interfaces::DataSource::Status::VALID, nil) |
| 47 | + rescue JSON::ParserError => e |
| 48 | + @config.logger.error { "[LDClient] JSON parsing failed for polling response." } |
| 49 | + error_info = LaunchDarkly::Interfaces::DataSource::ErrorInfo.new( |
| 50 | + LaunchDarkly::Interfaces::DataSource::ErrorInfo::INVALID_DATA, |
| 51 | + 0, |
| 52 | + e.to_s, |
| 53 | + Time.now |
| 54 | + ) |
| 55 | + @config.data_source_update_sink&.update_status(LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED, error_info) |
| 56 | + rescue Impl::DataSource::UnexpectedResponseError => e |
| 57 | + error_info = LaunchDarkly::Interfaces::DataSource::ErrorInfo.new( |
| 58 | + LaunchDarkly::Interfaces::DataSource::ErrorInfo::ERROR_RESPONSE, e.status, nil, Time.now) |
| 59 | + message = Util.http_error_message(e.status, "polling request", "will retry") |
| 60 | + @config.logger.error { "[LDClient] #{message}" } |
| 61 | + |
| 62 | + if Util.http_error_recoverable?(e.status) |
| 63 | + @config.data_source_update_sink&.update_status( |
| 64 | + LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED, |
| 65 | + error_info |
| 66 | + ) |
| 67 | + else |
| 68 | + @ready.set # if client was waiting on us, make it stop waiting - has no effect if already set |
| 69 | + stop_with_error_info error_info |
| 70 | + end |
| 71 | + rescue StandardError => e |
| 72 | + Util.log_exception(@config.logger, "Exception while polling", e) |
| 73 | + @config.data_source_update_sink&.update_status( |
| 74 | + LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED, |
| 75 | + LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(LaunchDarkly::Interfaces::DataSource::ErrorInfo::UNKNOWN, 0, e.to_s, Time.now) |
| 76 | + ) |
42 | 77 | end |
43 | 78 | end |
44 | | - @config.data_source_update_sink&.update_status(LaunchDarkly::Interfaces::DataSource::Status::VALID, nil) |
45 | | - rescue JSON::ParserError => e |
46 | | - @config.logger.error { "[LDClient] JSON parsing failed for polling response." } |
47 | | - error_info = LaunchDarkly::Interfaces::DataSource::ErrorInfo.new( |
48 | | - LaunchDarkly::Interfaces::DataSource::ErrorInfo::INVALID_DATA, |
49 | | - 0, |
50 | | - e.to_s, |
51 | | - Time.now |
52 | | - ) |
53 | | - @config.data_source_update_sink&.update_status(LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED, error_info) |
54 | | - rescue UnexpectedResponseError => e |
55 | | - error_info = LaunchDarkly::Interfaces::DataSource::ErrorInfo.new( |
56 | | - LaunchDarkly::Interfaces::DataSource::ErrorInfo::ERROR_RESPONSE, e.status, nil, Time.now) |
57 | | - message = Util.http_error_message(e.status, "polling request", "will retry") |
58 | | - @config.logger.error { "[LDClient] #{message}" } |
59 | 79 |
|
60 | | - if Util.http_error_recoverable?(e.status) |
61 | | - @config.data_source_update_sink&.update_status( |
62 | | - LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED, |
63 | | - error_info |
64 | | - ) |
65 | | - else |
66 | | - @ready.set # if client was waiting on us, make it stop waiting - has no effect if already set |
67 | | - stop_with_error_info error_info |
| 80 | + # |
| 81 | + # The original implementation of this class relied on the feature store |
| 82 | + # directly, which we are trying to move away from. Customers who might have |
| 83 | + # instantiated this directly for some reason wouldn't know they have to set |
| 84 | + # the config's sink manually, so we have to fall back to the store if the |
| 85 | + # sink isn't present. |
| 86 | + # |
| 87 | + # The next major release should be able to simplify this structure and |
| 88 | + # remove the need for fall back to the data store because the update sink |
| 89 | + # should always be present. |
| 90 | + # |
| 91 | + private def update_sink_or_data_store |
| 92 | + @config.data_source_update_sink || @config.feature_store |
68 | 93 | end |
69 | | - rescue StandardError => e |
70 | | - Util.log_exception(@config.logger, "Exception while polling", e) |
71 | | - @config.data_source_update_sink&.update_status( |
72 | | - LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED, |
73 | | - LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(LaunchDarkly::Interfaces::DataSource::ErrorInfo::UNKNOWN, 0, e.to_s, Time.now) |
74 | | - ) |
75 | | - end |
76 | | - end |
77 | | - |
78 | | - # |
79 | | - # The original implementation of this class relied on the feature store |
80 | | - # directly, which we are trying to move away from. Customers who might have |
81 | | - # instantiated this directly for some reason wouldn't know they have to set |
82 | | - # the config's sink manually, so we have to fall back to the store if the |
83 | | - # sink isn't present. |
84 | | - # |
85 | | - # The next major release should be able to simplify this structure and |
86 | | - # remove the need for fall back to the data store because the update sink |
87 | | - # should always be present. |
88 | | - # |
89 | | - private def update_sink_or_data_store |
90 | | - @config.data_source_update_sink || @config.feature_store |
91 | | - end |
92 | 94 |
|
93 | | - # |
94 | | - # @param [LaunchDarkly::Interfaces::DataSource::ErrorInfo, nil] error_info |
95 | | - # |
96 | | - private def stop_with_error_info(error_info = nil) |
97 | | - @task.stop |
98 | | - @config.logger.info { "[LDClient] Polling connection stopped" } |
99 | | - @config.data_source_update_sink&.update_status(LaunchDarkly::Interfaces::DataSource::Status::OFF, error_info) |
| 95 | + # |
| 96 | + # @param [LaunchDarkly::Interfaces::DataSource::ErrorInfo, nil] error_info |
| 97 | + # |
| 98 | + private def stop_with_error_info(error_info = nil) |
| 99 | + @task.stop |
| 100 | + @config.logger.info { "[LDClient] Polling connection stopped" } |
| 101 | + @config.data_source_update_sink&.update_status(LaunchDarkly::Interfaces::DataSource::Status::OFF, error_info) |
| 102 | + end |
| 103 | + end |
100 | 104 | end |
101 | 105 | end |
102 | 106 | end |
| 107 | + |
0 commit comments