Class: Html2rss::RequestService::Budget

Inherits:
Object
  • Object
show all
Defined in:
lib/html2rss/request_service/budget.rb

Overview

Tracks HTTP request slots and wall-clock deadline for one feed build.

HTTP fetches consume request slots. Deadline remains on #remaining_timeout_seconds.

Instance Method Summary collapse

Constructor Details

#initialize(max_requests:, total_timeout_seconds: nil) ⇒ Budget

Returns a new instance of Budget.

Parameters:

  • max_requests (Integer)

    maximum HTTP request slots

  • total_timeout_seconds (Integer, nil) (defaults to: nil)

    wall-clock timeout for the feed build



13
14
15
16
17
18
19
20
# File 'lib/html2rss/request_service/budget.rb', line 13

def initialize(max_requests:, total_timeout_seconds: nil)
  validate_slot_limits!(max_requests:)

  @remaining_requests = max_requests
  @start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  @total_timeout_seconds = total_timeout_seconds
  @mutex = Mutex.new
end

Instance Method Details

#consume!Integer

Consumes one HTTP request slot.

Returns:

  • (Integer)

    remaining request slots after consumption

Raises:



27
28
29
# File 'lib/html2rss/request_service/budget.rb', line 27

def consume!
  consume_request!
end

#consume_request!Integer

Consumes one HTTP request slot.

Returns:

  • (Integer)

    remaining request slots after consumption

Raises:



36
37
38
39
40
41
42
# File 'lib/html2rss/request_service/budget.rb', line 36

def consume_request!
  @mutex.synchronize do
    raise RequestBudgetExceeded, 'Request budget exhausted' if @remaining_requests.zero?

    @remaining_requests -= 1
  end
end

#effective_timeout_ms(fallback:) ⇒ Integer

Returns milliseconds available for this attempt.

Parameters:

  • fallback (Numeric)

    policy total timeout when remaining is untracked

Returns:

  • (Integer)

    milliseconds available for this attempt

Raises:



91
92
93
# File 'lib/html2rss/request_service/budget.rb', line 91

def effective_timeout_ms(fallback:)
  (effective_timeout_seconds(fallback:) * 1000).to_i
end

#effective_timeout_seconds(fallback:) ⇒ Float

Resolves wall-clock seconds for the next adapter attempt.

Prefers the tracked deadline remainder; falls back to the policy total when this Budget was constructed without a timeout (common in unit tests).

Parameters:

  • fallback (Numeric)

    policy total timeout when remaining is untracked

Returns:

  • (Float)

    seconds available for this attempt

Raises:



80
81
82
83
84
85
# File 'lib/html2rss/request_service/budget.rb', line 80

def effective_timeout_seconds(fallback:)
  timeout = remaining_timeout_seconds || fallback
  raise RequestTimedOut, 'Request timed out' if timeout <= 0

  timeout.to_f
end

#elapsed_secondsFloat

Returns seconds since this budget was created.

Returns:

  • (Float)

    seconds since this budget was created



67
68
69
# File 'lib/html2rss/request_service/budget.rb', line 67

def elapsed_seconds
  Process.clock_gettime(Process::CLOCK_MONOTONIC) - @start_time
end

#remainingInteger

Returns HTTP request slots still available.

Returns:

  • (Integer)

    HTTP request slots still available



46
47
48
# File 'lib/html2rss/request_service/budget.rb', line 46

def remaining
  remaining_requests
end

#remaining_requestsInteger

Returns HTTP request slots still available.

Returns:

  • (Integer)

    HTTP request slots still available



52
53
54
# File 'lib/html2rss/request_service/budget.rb', line 52

def remaining_requests
  @mutex.synchronize { @remaining_requests }
end

#remaining_timeout_secondsFloat?

Returns the remaining timeout in seconds, or nil if not tracked.

Returns:

  • (Float, nil)

    the remaining timeout in seconds, or nil if not tracked



58
59
60
61
62
63
# File 'lib/html2rss/request_service/budget.rb', line 58

def remaining_timeout_seconds
  return unless @total_timeout_seconds

  remaining = @total_timeout_seconds - elapsed_seconds
  [remaining, 0.0].max
end