Skip to content

feat: Add file loading code for flag overrides - #449

Draft
kinyoklion wants to merge 1 commit into
feat/overridesfrom
rlamb/overrides-ruby-filedata
Draft

kinyoklion wants to merge 1 commit into
feat/overridesfrom
rlamb/overrides-ruby-filedata

Conversation

@kinyoklion

@kinyoklion kinyoklion commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

Summary

This is the first of a series that adds flag overrides, as defined by the OVERRIDE specification, to the Ruby server SDK. The existing FDv1 and FDv2 file data sources keep their current behavior: this PR does not change lib/ldclient-rb/impl/integrations/file_data_source.rb, file_data_source_v2.rb, or their specs, and the override feature is additive throughout the series.

The file-based override source must tolerate a missing file, a file that is being written, and a burst of change notifications, and it must keep the last good data when a reload fails. This change adds LaunchDarkly::Impl::FileData, the file reading, parsing, merging, and reloading code that source needs:

  • Document parsing for JSON and YAML. The parser checks that the document and its flags, flagValues, and segments members are objects.
  • An ordered merge of several documents with duplicate keys handling of fail or ignore, expansion of each flagValues entry into a flag that is off and serves the value (so an evaluation reports the OFF reason kind, as the specification describes), deserialization into the data model classes, and per-document entry counts.
  • A reloader that serializes reloads, debounces change signals, keeps the last good result when a reload fails, retries after a bounded delay, reports each distinct failure once, and can skip a reload whose content is byte-identical to the last applied content. A configured file that does not exist can be treated as a file with no content.
  • A stat-based poller that compares existence, modification time, and size. A file that appears or disappears is a change.
  • A watcher that uses rb-inotify on Linux (a dependency of the optional listen gem) to watch the directory of each file without descending into subdirectories, and listen elsewhere. It picks up a file that does not exist yet when it appears, and it retries once per second when a directory does not exist. Non-recursive watching matters because listen scans the whole tree under a watched directory and fails when a subdirectory cannot be read, as happens for a file in the system temporary directory.

A new spec, spec/integrations/file_data_source_compatibility_spec.rb, pins the behaviors of the existing file data sources that their other specs did not assert (per-file version numbering, keying by the entry's own key, the on-plus-fallthrough expansion of flagValues, no retry of a failed load, log messages, modification-time-only polling, no reaction to a deleted file when polling, the FDv1 poller's reload cadence after a change, and use of listen when it is present), so that they stay as they are while this code exists beside them.

Verification: unit specs for the document parser, merge, poller, reloader (including debounce, retry, last-good retention, sequential reloads, and stop), and watcher (including a directory with an unreadable subdirectory and the inotify thread lifecycle), the new compatibility pins, and the unchanged existing file data source specs. Full suite and RuboCop are clean. Each new spec was checked against a deliberate defect in the code it covers, and each compatibility pin against a deliberate change to the existing source it pins.

Adds `LaunchDarkly::Impl::FileData`, the file reading, parsing, merging,
and reloading code that the file-based override source defined by the
OVERRIDE specification needs. That source must tolerate a missing file,
a file that is being written, and a burst of change notifications, and
it must keep the last good data when a reload fails. The module provides:

- Document parsing for JSON and YAML with validation that the document
  and its flags, flagValues, and segments members are objects.
- An ordered merge of several documents with duplicate keys handling of
  fail or ignore, flagValues expansion into off flags that serve the
  value, model deserialization, and per-document entry counts.
- A reloader that serializes reloads, debounces change signals, retains
  the last good result when a reload fails, retries after a bounded
  delay, reports each distinct failure once, and can skip byte-identical
  reloads. A configured file that does not exist can be treated as a
  file with no content.
- A stat-based poller that compares existence, modification time, and
  size, so a file that appears or disappears is a change.
- A watcher that uses rb-inotify on Linux to watch the directory of each
  file without descending into subdirectories, and the listen gem
  elsewhere, so a file that does not exist yet is picked up when it
  appears. It retries when a directory does not exist.

The existing FDv1 and FDv2 file data sources are not changed. They keep
their own implementation and behavior. A new spec pins the behaviors of
those sources that their other specs did not assert, so that they stay
as they are while this code exists beside them.
@kinyoklion
kinyoklion force-pushed the rlamb/overrides-ruby-filedata branch from eb26dd7 to 72d5776 Compare September 28, 2026 20:36
@kinyoklion kinyoklion changed the title feat: Make file data loading reliable and shared feat: Add file loading code for flag overrides Sep 28, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant