Class: OpenASN::OverlayStore
- Inherits:
-
Object
- Object
- OpenASN::OverlayStore
- Defined in:
- lib/openasn/overlay_store.rb
Overview
On-disk store for Tier B overlays under data_dir/overlays/.
Files: source_id-ipv4.bin / source_id-ipv6.bin — raw concatenated
big-endian (start, end) pairs, sorted and merged. No header: these are
internal files owned by this gem, versioned by the schema field in
state.json, and always written atomically (tmp + rename) so a reader
process can never observe a torn file.
state.json tracks per-source metadata: what it maps to, when it was fetched, ETag for conditional GETs, and the last error (keep-stale semantics: a failing source keeps serving its previous data, loudly).
Defined Under Namespace
Classes: Entry
Constant Summary collapse
- SCHEMA =
1
Instance Method Summary collapse
- #clear!(id) ⇒ Object
- #fetched_at(id) ⇒ Object
-
#initialize(data_dir) ⇒ OverlayStore
constructor
A new instance of OverlayStore.
-
#load(enabled_ids, mode) ⇒ Object
Load every stored overlay among enabled_ids into memory.
- #record_failure(id, error_message) ⇒ Object
- #record_fresh(id) ⇒ Object
- #source_state(id) ⇒ Object
- #state ⇒ Object
-
#write(id, maps_to:, provider: nil, etag: nil, ranges_by_family:) ⇒ Object
ranges_by_family: { ipv4: [[s,e],...] (sorted, merged), ipv6: [...] }.
Constructor Details
#initialize(data_dir) ⇒ OverlayStore
Returns a new instance of OverlayStore.
23 24 25 26 |
# File 'lib/openasn/overlay_store.rb', line 23 def initialize(data_dir) @dir = File.join(data_dir, "overlays") @state_path = File.join(@dir, "state.json") end |
Instance Method Details
#clear!(id) ⇒ Object
97 98 99 |
# File 'lib/openasn/overlay_store.rb', line 97 def clear!(id) %i[ipv4 ipv6].each { |f| FileUtils.rm_f(file_path(id, f)) } end |
#fetched_at(id) ⇒ Object
74 75 76 77 78 79 |
# File 'lib/openasn/overlay_store.rb', line 74 def fetched_at(id) ts = source_state(id)["fetched_at"] ts && Time.parse(ts) rescue ArgumentError nil end |
#load(enabled_ids, mode) ⇒ Object
Load every stored overlay among enabled_ids into memory.
82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/openasn/overlay_store.rb', line 82 def load(enabled_ids, mode) sources = state["sources"] enabled_ids.filter_map do |id| = sources[id] next unless v4 = load_family(id, :ipv4, mode) v6 = load_family(id, :ipv6, mode) next unless v4 || v6 Entry.new(id: id, maps_to: ["maps_to"], provider: ["provider"], v4: v4, v6: v6) end end |
#record_failure(id, error_message) ⇒ Object
62 63 64 65 66 |
# File 'lib/openasn/overlay_store.rb', line 62 def record_failure(id, ) update_state(id) do |entry| entry.merge("last_error" => , "last_attempt_at" => Time.now.utc.iso8601) end end |
#record_fresh(id) ⇒ Object
68 69 70 71 72 |
# File 'lib/openasn/overlay_store.rb', line 68 def record_fresh(id) update_state(id) do |entry| entry.merge("fetched_at" => Time.now.utc.iso8601, "last_error" => nil) end end |
#source_state(id) ⇒ Object
38 |
# File 'lib/openasn/overlay_store.rb', line 38 def source_state(id) = state.dig("sources", id) || {} |
#state ⇒ Object
28 29 30 31 32 33 34 35 36 |
# File 'lib/openasn/overlay_store.rb', line 28 def state return { "schema" => SCHEMA, "sources" => {} } unless File.exist?(@state_path) parsed = JSON.parse(File.read(@state_path)) # Unknown future schema: treat as empty rather than misread it. parsed["schema"] == SCHEMA ? parsed : { "schema" => SCHEMA, "sources" => {} } rescue JSON::ParserError { "schema" => SCHEMA, "sources" => {} } end |
#write(id, maps_to:, provider: nil, etag: nil, ranges_by_family:) ⇒ Object
ranges_by_family: { ipv4: [[s,e],...] (sorted, merged), ipv6: [...] }
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/openasn/overlay_store.rb', line 41 def write(id, maps_to:, provider: nil, etag: nil, ranges_by_family:) FileUtils.mkdir_p(@dir) counts = {} %i[ipv4 ipv6].each do |family| ranges = ranges_by_family[family] || [] counts[family] = ranges.length packed = ranges.map { |(s, e)| BinaryFormat.pack_addr(s, family) + BinaryFormat.pack_addr(e, family) }.join path = file_path(id, family) File.binwrite("#{path}.tmp", packed) File.rename("#{path}.tmp", path) end update_state(id) do |entry| entry.merge( "maps_to" => maps_to.to_s, "provider" => provider, "etag" => etag, "fetched_at" => Time.now.utc.iso8601, "records_ipv4" => counts[:ipv4], "records_ipv6" => counts[:ipv6], "last_error" => nil ) end end |