Class: OpenASN::OverlayStore

Inherits:
Object
  • Object
show all
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

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|
    meta = sources[id]
    next unless meta

    v4 = load_family(id, :ipv4, mode)
    v6 = load_family(id, :ipv6, mode)
    next unless v4 || v6

    Entry.new(id: id, maps_to: meta["maps_to"], provider: meta["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, error_message)
  update_state(id) do |entry|
    entry.merge("last_error" => error_message, "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) || {}

#stateObject



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