Repository navigation
Fix worker identity in partial YARN logs #279
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from 5 commits
70aca84
44e9bb7
2ea6867
e589f5f
f87e3ee
16c6711
9d9f79b
b0920a5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,7 +42,7 @@ | |
| r"totalAllocatedContainers=(?P<containers>\d+)" | ||
| ) | ||
| NODE_RE = re.compile( | ||
| r"Registered with ResourceManager .* total resource of " | ||
| r"Registered with ResourceManager(?: as (?P<host>[^:,\s]+):\d+)? .* total resource of " | ||
| r"<memory:(?P<memory>\d+), vCores:(?P<vcores>\d+)(?P<resources>[^>]*)>" | ||
| ) | ||
| RM_NODE_RE = re.compile( | ||
|
|
@@ -257,13 +257,16 @@ def parse_yarn_logs(path: Path) -> YarnEvidence: | |
| evidence = YarnEvidence() | ||
| rm_finishes: dict[str, int] = {} | ||
| nm_finishes: dict[str, int] = {} | ||
| nm_hosts_by_dir: dict[Path, set[str]] = {} | ||
| unresolved_nm_containers: dict[Path, list[Container]] = {} | ||
| files = relevant_log_files(path) | ||
| if not files: | ||
| raise ValueError(f"No ResourceManager or NodeManager log files found under {path}") | ||
|
|
||
| for file in files: | ||
| path_node_id = file.parent.name | ||
| path_node = evidence.nodes.setdefault(path_node_id, Node(path_node_id)) | ||
| node_id = path_node_id | ||
| node = evidence.nodes.setdefault(node_id, Node(node_id)) | ||
| with open_log(file) as handle: | ||
| for raw_line in handle: | ||
| line = normalize_log_line(raw_line) | ||
|
|
@@ -324,17 +327,24 @@ def parse_yarn_logs(path: Path) -> YarnEvidence: | |
| int(node_match.group("vcores")), | ||
| node_match.group("resources"), | ||
| ) | ||
| path_node.instance_type = _node_instance_type(line) | ||
| path_node.memory_mb = resources["memory-mb"] | ||
| path_node.vcores = resources["vcores"] | ||
| path_node.gpus = resources.get("yarn.io/gpu", 0) | ||
| path_node.resources = resources | ||
| node_id = node_match.group("host") or path_node_id | ||
| node = evidence.nodes.setdefault(node_id, Node(node_id)) | ||
| if node_match.group("host"): | ||
| nm_hosts_by_dir.setdefault(file.parent, set()).add(node_id) | ||
| for container in unresolved_nm_containers.pop(file, []): | ||
| container.node_id = node_id | ||
| node.instance_type = _node_instance_type(line) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we preserve the existing instance type when this NM line doesn’t include one? Otherwise it clears a class already reported by the RM and can prevent pricing.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed on |
||
| node.memory_mb = resources["memory-mb"] | ||
| node.vcores = resources["vcores"] | ||
| node.gpus = resources.get("yarn.io/gpu", 0) | ||
| node.resources = resources | ||
| continue | ||
| assignment = RM_ASSIGN_RE.search(line) | ||
| if assignment: | ||
| host = assignment.group("host") | ||
| assigned_node = evidence.nodes.setdefault(host, Node(host)) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we keep the host in the node inventory without treating this placeholder as registration evidence? With assignment maxima and a node-class map, this now produces a final cost even though no node registration was logged.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed on the current head |
||
| candidate = _container_from_match( | ||
| assignment, host, evidence.nodes.get(host), "resourcemanager" | ||
| assignment, host, assigned_node, "resourcemanager" | ||
| ) | ||
| evidence.containers[candidate.container_id] = candidate | ||
| continue | ||
|
|
@@ -349,9 +359,13 @@ def parse_yarn_logs(path: Path) -> YarnEvidence: | |
| start = START_RE.search(line) | ||
| if start: | ||
| candidate = _container_from_match( | ||
| start, path_node_id, path_node, "nodemanager" | ||
| start, node_id, node, "nodemanager" | ||
| ) | ||
| evidence.containers.setdefault(candidate.container_id, candidate) | ||
| container = evidence.containers.setdefault( | ||
| candidate.container_id, candidate | ||
| ) | ||
| if container is candidate and node_id == path_node_id: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we track whether this file has reported a host instead of comparing
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed on |
||
| unresolved_nm_containers.setdefault(file, []).append(container) | ||
| continue | ||
| done = DONE_RE.search(line) | ||
| if done: | ||
|
|
@@ -361,6 +375,20 @@ def parse_yarn_logs(path: Path) -> YarnEvidence: | |
| finish, nm_finishes.get(container_id, finish) | ||
| ) | ||
|
|
||
| # A rotated single-worker archive can put registration and starts in | ||
| # different files. Infer across files only when the directory contains one | ||
| # unambiguous reported NodeManager host. With multiple workers, keep the | ||
| # path-derived identity unknown rather than assigning another worker's | ||
| # capacity. | ||
| for unresolved_file, containers in unresolved_nm_containers.items(): | ||
| hosts = nm_hosts_by_dir.get(unresolved_file.parent, set()) | ||
| if len(hosts) != 1: | ||
| continue | ||
| host = next(iter(hosts)) | ||
| for container in containers: | ||
| if container.node_id == unresolved_file.parent.name: | ||
| container.node_id = host | ||
|
|
||
| for container_id, container in evidence.containers.items(): | ||
| # Registration evidence may be stored in a later file than the | ||
| # allocation. Reconcile containers after every archived log was parsed. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.