Class: ReactOnRailsPro::RollingDeployAdapters::Http

Inherits:
Object
  • Object
show all
Defined in:
lib/react_on_rails_pro/rolling_deploy_adapters/http.rb

Overview

Built-in HTTP rolling-deploy adapter. Pairs with ReactOnRailsPro::RollingDeploy::BundlesController on the running Rails server: the controller exposes the current deployment's bundles and the adapter (running in the next deployment's build CI) fetches them.

The promise is "zero-infra default": no S3 bucket, no IAM, no extra gem. The currently-deployed Rails server already has the bundles + companion assets sitting on disk; this adapter pulls them via authenticated HTTP.

Configuration (see docs/pro/rolling-deploy-adapters.md):

ReactOnRailsPro.configure do |config|
config.rolling_deploy_adapter       = ReactOnRailsPro::RollingDeployAdapters::Http
config.rolling_deploy_token         = ENV.fetch("ROLLING_DEPLOY_TOKEN")
config.rolling_deploy_previous_urls = ENV["ROLLING_DEPLOY_PREVIOUS_URLS"]
end

rolling_deploy_previous_urls accepts a single URL string, a comma- separated string, or an Array of URL strings. Discovery unions the bundle hashes advertised by every endpoint; fetch tries each endpoint in order and returns the first that has the requested hash. Seeding from more than one endpoint (e.g. staging + production) lets an image built in one environment and promoted to another carry the promotion target's draining bundle. See docs/pro/rolling-deploy-adapters.md#promotion-deploys-need-a-release-time-boot-seed.

Error contract matches the rolling_deploy_adapter protocol: every exception is caught and reported as a warning so a failed seed degrades to the runtime 410-retry fallback rather than failing the build. rubocop:disable Metrics/ClassLength

Constant Summary collapse

DEFAULT_OPEN_TIMEOUT_SECONDS =

Per-request HTTP timeouts. The outer Timeout.timeout in RollingDeployCacheStager bounds the total wall-clock budget (10s for discovery, 30s for fetch); these inner timeouts let a hung server fail before the outer wrapper interrupts mid-write, which is more reliable than relying on the thread-level Timeout.timeout that may interrupt at a random execution point.

5
DEFAULT_READ_TIMEOUT_SECONDS =
25
MANIFEST_READ_TIMEOUT_SECONDS =

Manifest discovery is wrapped in a 10s outer budget by RollingDeployCacheStager.

4
DEFAULT_MAX_SIZE =

Maximum uncompressed payload accepted from /bundles/:hash. Mirrors the tarball helper default so a misbehaving or malicious server cannot exhaust disk via a zip-bomb-style response.

ReactOnRailsPro::RollingDeploy::Tarball::DEFAULT_MAX_SIZE
COMPRESSED_BODY_CAP =

Maximum compressed bytes accepted from /bundles/:hash before extract enforces DEFAULT_MAX_SIZE on the uncompressed tarball contents. Set near 1/4 of DEFAULT_MAX_SIZE: JS bundles typically decompress 3-5x, so a 50 MB wire payload that decompresses beyond 200 MB is anomalous.

50 * 1024 * 1024
LOG_PREFIX =
"[ReactOnRailsPro::RollingDeployAdapters::Http]"
BUNDLE_ENTRY_NAME =

Wire-format constant: must stay in sync with ReactOnRailsPro::RollingDeploy::BundlesController::BUNDLE_ENTRY_NAME. The controller serves the bundle file under this entry name; if the two ever diverge the client will fail to locate the bundle after extracting the tarball.

"bundle.js"

Class Method Summary collapse

Class Method Details

.fetch(bundle_hash) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/react_on_rails_pro/rolling_deploy_adapters/http.rb', line 107

def fetch(bundle_hash)
  return nil if hash_invalid?(bundle_hash)

  bases = configured_previous_urls
  return nil if bases.empty?

  if token_missing?
    return warn_and_return("rolling_deploy_token is not configured; skipping fetch(#{bundle_hash.inspect})",
                           nil)
  end

  # Try each endpoint in order; return the first that has the hash. A
  # given hash is content-addressed, so whichever endpoint serves it
  # returns identical bytes — first hit wins.
  bases.each do |base|
    payload = fetch_from(base, bundle_hash)
    return payload if payload
  end
  nil
end

.previous_bundle_hashesObject



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/react_on_rails_pro/rolling_deploy_adapters/http.rb', line 91

def previous_bundle_hashes
  bases = configured_previous_urls
  return [] if bases.empty?

  if token_missing?
    return warn_and_return("rolling_deploy_token is not configured; skipping manifest fetch",
                           [])
  end

  # Union across every configured endpoint. A single endpoint failing
  # (down, 404, malformed manifest) yields [] for that base and does not
  # abort discovery for the others — the point of multiple endpoints is
  # resilience, so one unreachable source must not blank the rest.
  bases.flat_map { |base| manifest_hashes(base) }.uniq
end

.upload(_bundle_hash, bundle:, assets:) ⇒ Object

Intentional no-op. The running Rails server IS the artifact store — bundle + companion assets are already on local disk where the mountable BundlesController will serve them on the next deploy's build CI. Documented in docs/pro/rolling-deploy-adapters.md.



132
133
134
# File 'lib/react_on_rails_pro/rolling_deploy_adapters/http.rb', line 132

def upload(_bundle_hash, bundle:, assets:)
  # See class doc above.
end