Skip to content

Change to allow passing s3 uri to pick the logs from a separate s3 directory and limit to just resource manager logs - #277

Merged
gerashegalov merged 10 commits into
NVIDIA:devfrom
tgravescs:yarn-rm-log-uri
Oct 1, 2026
Merged

gerashegalov merged 10 commits into
NVIDIA:devfrom
tgravescs:yarn-rm-log-uri

Conversation

@tgravescs

Copy link
Copy Markdown
Collaborator

The emr log pusher defaults to push logs to s3 at a 5 minutes interval. It isn't configurable. Some people want this more frequent and could push logs with fluent bit or other utility. Allow reading from another s3 directory and only looking at resource manager logs.

calculate_yarn_job_cost.py has not been manually tested by me.

Signed-off-by: Thomas Graves <tgraves@nvidia.com>
@greptile-apps

greptile-apps Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[Medium risk] Adds optional log source selection to cost calculation.

The PR appears safe to merge, with a non-blocking CLI input-validation gap.

Findings

  1. P2 Empty S3 bucket accepted ▶

Summary

The PR lets the API and CLI prefer shipped ResourceManager logs over the EMR archive, with archive fallback and a resolved-source field in CLI JSON output.

  • Adds log-source selection and validation tests.
  • Documents the shipper URI option and fallback behavior.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Event log or application request] --> B{Shipper URI supplied?}
  B -->|Yes| C[Read shipped ResourceManager logs]
  C -->|No usable logs| D[Read EMR LogUri archive]
  B -->|No| D
  C -->|Usable logs| E[Calculate usage]
  D --> E
Loading

Reviews (7) · Last reviewed commit: "Reject non-S3 --yarn-log-shipper-uri and..."

Comment thread yarn-resource-cost/yarn_job_cost_api.py Resolved
Comment thread yarn-resource-cost/yarn_job_cost_api.py Resolved
Zero-byte ResourceManager logs from the log shipper no longer count as
downloaded, so both the API and the CLI fall back to the EMR log URI.
Refreshing a log cache now removes the previous snapshot so logs rotated
or deleted upstream are not kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Thomas Graves <tgraves@nvidia.com>
tgravescs and others added 2 commits September 29, 2026 15:56
Refresh now only removes the .download-complete marker, so drop the
stale-file assertion.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@gerashegalov gerashegalov left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with the reproducibility concern #277 (comment)

Shipper source missing from JSON When shipped logs supply an
--event-log-root run, this branch clears emr_log_uri, but the JSON
output does not record --yarn-log-shipper-uri instead. The result has no
S3 source for its YARN evidence, making the cost calculation harder to
trace or reproduce.

Signed-off-by: Thomas Graves <tgraves@nvidia.com>
@gerashegalov
gerashegalov self-requested a review September 30, 2026 20:22
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
tgravescs and others added 2 commits September 30, 2026 15:59
materialize_yarn_logs now returns the S3 URI it actually read from
(log shipper or EMR LogUri) alongside the local path, so --output-json
can record it. Add a test for source selection and assert the field in
the end-to-end JSON test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A local shipper path skipped the empty-log check and never fell back to
the EMR log archive. Match the API, which already requires an S3 URI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment on lines +2021 to +2024
if args.yarn_log_shipper_uri is not None and not args.yarn_log_shipper_uri.startswith(
("s3://", "s3a://", "s3n://")
):
parser.error("--yarn-log-shipper-uri must be an S3 URI")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Empty S3 bucket accepted An empty-bucket value such as s3:///logs passes this scheme-only check, even though the API rejects it as an invalid S3 URI. The CLI then passes it to aws s3 cp, so a typo fails during log download rather than being rejected as an invalid --yarn-log-shipper-uri argument.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, to defer to a follow up

@gerashegalov gerashegalov left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@gerashegalov
gerashegalov merged commit 3f8a5b4 into NVIDIA:dev Oct 1, 2026
6 checks passed
gerashegalov added a commit that referenced this pull request Oct 5, 2026
## Summary

Add opt-in, application-scoped incremental ResourceManager accounting
for long-running EMR clusters. Repeated attempts currently re-download
and parse historical cluster logs; this change retains completed work
and avoids NodeManager fallback in the low-latency mode.

- Add `rm_only=True`, a persistent `cache_dir`, and a cooperative
`timeout_seconds` budget to the Python API. A configured log-shipper
prefix is authoritative: missing RM evidence returns retryable pending
without scanning the full EMR archive or reading Spark event logs
prematurely. The existing diagnostic/default path remains available.
- Cache normalized RM records in a per-source SQLite index, refreshing
only new or changed objects and selecting the requested application's
records plus historical node/scheduler metadata. Use conditional GETs,
atomic per-object updates, deletion reconciliation, and a final snapshot
check to prevent partial or stale evidence from becoming complete usage.
- Preserve complete Spark event-log downloads and per-segment parsing
checkpoints across attempts. Invalidate successor checkpoints on source
changes, prioritize cold RM objects, and let event work progress after
the target RM summary has been observed. After prioritized event work,
list a fresh full RM snapshot rather than reusing stale ETags from the
initial availability probe.
- Add phase timing and object/byte/cache-reuse diagnostics, regression
coverage, packaging support, and operating guidance.
- Fence RM listings against concurrent index writes, reject event
downloads whose byte count differs from the listed object, and preserve
unbounded processing for the original API mode when no timeout is
requested.
- Treat SQLite lock contention and interrupted queries as retryable;
propagate permanent cache/storage failures instead of reporting them as
missing logs. Python 3.10 is covered by a narrow fallback because its
SQLite exceptions lack error codes.

Builds on the log-shipper API in #277. Resource-seconds and accounting
completeness semantics are unchanged; the caller still owns pricing and
retry scheduling.

## Validation

- `python -m pytest -q yarn-resource-cost`: **119 passed, 5 subtests
passed** locally on Python 3.11; PR CI runs Python 3.10, 3.11, and 3.12.
- `python -m py_compile yarn-resource-cost/*.py`: passed.
- `package_yarn_job_cost.py` standalone bundle build and verification:
passed on the latest change.
- `git diff --check`: passed on the latest change.
- Independent local review exercised conditional-GET
replacement/deletion races, concurrent index refreshes, multi-page
listings, checkpoint invalidation, and bounded-retry progress. A
deterministic periodic-upload reproduction completes on attempt two in
**17 simulated seconds**, with zero conditional GET failures. This is a
regression check, not a production benchmark.
- Review regressions reproduce a stale RM listing after a newer index
write, a silent short event-log read, an oversized legacy archive, and a
malformed persistent SQLite cache. Tests verify that only transient
conditions return pending and that permanent cache errors propagate.

## Operational limits

In RM-only mode, the omitted budget defaults to 120 seconds; the
original mode remains unbounded unless a timeout is explicitly supplied.
The budget is cooperative, not a hard deadline; injected SDK clients
need bounded I/O settings. Each object download or segment parse must
fit an attempt, and the final mutable RM snapshot plus warm-cache
overhead must also fit. Cold calls still scan RM history; warm calls
still list the prefix and process global metadata. The disposable cache
requires private local storage with SQLite locking and has no automatic
TTL or size eviction; it also retains full Spark event-log objects.
Permanent cache errors require operator attention or a stopped-caller
cache rebuild. Live AWS and representative production-scale cold/warm
latency have not been measured.

---------

Signed-off-by: Gera Shegalov <gshegalov@nvidia.com>
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.

2 participants