Skip to content

SPARK-1623. Broadcast cleaner should use getCanonicalPath when deleting files by name - #546

Closed
nsuthar wants to merge 5 commits into
apache:masterfrom
nsuthar:master
Closed

nsuthar wants to merge 5 commits into
apache:masterfrom
nsuthar:master

Conversation

@nsuthar

@nsuthar nsuthar commented Apr 25, 2014

Copy link
Copy Markdown

Getting absloute path instead of relative path. : Its a same bug as Jira 1527

nirajguavus and others added 4 commits April 23, 2014 16:23
Details: rootDirs in DiskBlockManagerSuite doesn't get full path from rootDir0, rootDir1
rootDirs in DiskBlockManagerSuite doesn't get full path from rootDir0,
rootDir1
Its same issue as Jira 1527
- fixed it to use absolute path instead of relative
@AmplabJenkins

Copy link
Copy Markdown

Can one of the admins verify this patch?

@pwendell

Copy link
Copy Markdown
Contributor

Jenkins, test this please.

@AmplabJenkins

Copy link
Copy Markdown

Merged build triggered.

@AmplabJenkins

Copy link
Copy Markdown

Merged build started.

@AmplabJenkins

Copy link
Copy Markdown

Merged build finished.

@AmplabJenkins

Copy link
Copy Markdown

Refer to this link for build results: https://amplab.cs.berkeley.edu/jenkins/job/SparkPullRequestBuilder/14472/

@pwendell

Copy link
Copy Markdown
Contributor

Mind changing the name? I created a new JIRA for this:
https://issues.apache.org/jira/browse/SPARK-1623

Also - the fix as-is doesn't seem to compile.

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.

Shouldn't this be new File(file.toString).getCanonicalPath ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(Removed my earlier incorrect comment) @techaddict is correct since the value file is a String. Niraj your two new versions are either create a String argument where a File is needed or call File methods on a String. I think the correct invocation is simply deleteBroadcastFile(new File(file)). Everything else would be superfluous. (And this too would simplify if the set of values contained Files not Strings of paths.)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Sure Sean, I think I agree with you cause hashSet entries are string:

private val files = new TimeStampedHashSet[String]

and I am making it to deleteBroadcastFile(new File(file)) as per your
suggestion.

Niraj

On Thu, Apr 24, 2014 at 10:52 PM, Sean Owen [email protected]:

In core/src/main/scala/org/apache/spark/broadcast/HttpBroadcast.scala:

@@ -229,7 +229,7 @@ private[spark] object HttpBroadcast extends Logging {
val (file, time) = (entry.getKey, entry.getValue)
if (time < cleanupTime) {
iterator.remove()

  •    deleteBroadcastFile(new File(file.toString))
    
  •    deleteBroadcastFile(new File(file.getCanonicalPath))
    

(Removed my earlier incorrect comment) @techaddicthttps://github.com/techaddictis correct since the value
file is a String. Niraj your two new versions are either create a Stringargument where a
File is needed or call File methods on a String. I think the correct
invocation is simply deleteBroadcastFile(new File(file)). Everything else
would be superfluous. (And this too would simplify if the set of values
contained Files not Strings of paths.)

—
Reply to this email directly or view it on GitHubhttps://github.com//pull/546/files#r11983954
.

@nsuthar

nsuthar commented Apr 25, 2014

Copy link
Copy Markdown
Author

Hi Patrick,

By changing the name..I suppose you meant.."Assignee Name". I have assigned
it to myself.

Please correct me if i am wrong.
Thank you,
Niraj

On Thu, Apr 24, 2014 at 8:13 PM, Patrick Wendell
[email protected]:

Mind changing the name? I created a new JIRA for this:
https://issues.apache.org/jira/browse/SPARK-1623

Also - the fix as-is doesn't seem to compile.

—
Reply to this email directly or view it on GitHubhttps://github.com//pull/546#issuecomment-41355382
.

@pwendell

Copy link
Copy Markdown
Contributor

@nsuthar ah I wasn't clear - I meant change the title of this pull request to have SPARK-1623 at the front of the title.

@srowen

srowen commented Apr 25, 2014

Copy link
Copy Markdown
Member

@nsuthar pretty sure he meant the name of the PR, to something like
SPARK-1623. Broadcast cleaner should use getCanonicalPath when deleting files by name
(Note spelling of Canonical ! :) )

@nsuthar nsuthar changed the title Getting absloute path instead of relative path. SPARK-1623: Getting absloute path instead of relative path. Apr 25, 2014
@nsuthar

nsuthar commented Apr 25, 2014

Copy link
Copy Markdown
Author

OK, Sorry I misunderstood it. did it.

On Thu, Apr 24, 2014 at 10:44 PM, Sean Owen [email protected]:

@nsuthar https://github.com/nsuthar pretty sure he meant the name of
the PR, to something like
SPARK-1623. Broadcast cleaner should use getCanonicalPath when deleting
files by name
(Note spelling of Canonical ! :) )

—
Reply to this email directly or view it on GitHubhttps://github.com//pull/546#issuecomment-41360674
.

@nsuthar nsuthar changed the title SPARK-1623: Getting absloute path instead of relative path. SPARK-1623. Broadcast cleaner should use getCanonicalPath when deleting files by name Apr 25, 2014
SPARK-1623. Broadcast cleaner should use getCanonicalPath when deleting
files by name
@nsuthar

nsuthar commented Apr 25, 2014

Copy link
Copy Markdown
Author

fixed it to get abs path....jira 1623

@nsuthar

nsuthar commented Apr 25, 2014

Copy link
Copy Markdown
Author

could someone pls verify this patch. Thanks.

Niraj

@tdas

tdas commented Apr 25, 2014

Copy link
Copy Markdown
Contributor

Jenkins, test this please.

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.

not required to wrap it in another File.

@mridulm

mridulm commented Apr 27, 2014

Copy link
Copy Markdown
Contributor

Are you actually seeing problems or is this a cleanup exercise to use appropriate api ?
Creation of the file happens from within spark and is not externally provided - and that should match how it gets used.

If we are not seeing any actual issues, I would rather not go down the path of fixing this.
getCanonicalPath, etc are expensive operations reqiuring filesystem IO to resolve.

@srowen

srowen commented Apr 27, 2014

Copy link
Copy Markdown
Member

getAbsolutePath() would also resolve both issues as far as I can tell,
and does not involve I/O. IIRC there was a real issue observed in
DiskBlockManagerSuite.

For HttpBroadcast, see how the file names are retrieved with
getAbsolutePath(), so this is a real issue albeit probably theoretical
right now. (That one's also resolved by just using a Set of Files.)

(Is the I/O going to matter though? These are executed once.)

On Sun, Apr 27, 2014 at 3:06 PM, Mridul Muralidharan
[email protected] wrote:

Are you actually seeing problems or is this a cleanup exercise to use
appropriate api ?
Creation of the file happens from within spark and is not externally
provided - and that should match how it gets used.

If we are not seeing any actual issues, I would rather not go down the path
of fixing this.
getCanonicalPath, etc are expensive operations reqiuring filesystem IO to
resolve.

—
Reply to this email directly or view it on GitHub.

@mridulm

mridulm commented Apr 27, 2014

Copy link
Copy Markdown
Contributor

It goes back to the problem we are trying to solve.
If the set/map can contain arbitrary paths then file.getCanonical is unavoidable.
But then (multiple) IO and native call overhead will be there

If it is in context of a specific usecase - then it becomes function of that : do we need canonical names or are the path always constructed using common patterns which are consistent (always relative to cwd or specified w.r.t some root (which can be symlink).
IMO spark does the latter - unless there are recent changes I am missing.

In any case, getAbsolutePath/File does not buy us anything much.

@srowen

srowen commented Apr 27, 2014

Copy link
Copy Markdown
Member

Agree and that leads to the changes. For example HttpBroadcast.scala adds paths to the set using the string from getAbsolutePath (line 173) but removes them with the value of toString, which is getPath, which is not necessarily the same. (It may happen to be here.) Seems better to be consistent. In DiskBlockManagerSuite.scala I also don't see we have a guarantee that the path is absolute, and it needs to be IIUC, and the OP suggests this doesn't actually work in some cases.

@pwendell

Copy link
Copy Markdown
Contributor

Just fly-by-commenting here, but the performance cost of doing a few directory traversals is not relevant in this code path. It gets called only once in the lifetime of a broadcast variable. It think the bigger issues are around correctness and consistency...

@mridulm

mridulm commented May 3, 2014

Copy link
Copy Markdown
Contributor

It is not about a few uses here or there - either spark codebase as a whole moves to a) canonical path always; or always sticks to b) paths relative to cwd and/or what is returned by File.createTempFile - there is no middle ground IMO.
(b) is where we are at currently with some bugs as @srowen mentioned. Either we fix those to conform to (b) or move to (a) entirely.

Btw, regarding cost - we do quite a lot of path manipulations elsewhere (block management, shuffle, etc) - which adds to the cost.

Trying to carefully cordon off different sections of the code to different idioms is just asking for bugs as the codebase evolves. As we are apparently already hitting !

@pwendell

pwendell commented May 7, 2014

Copy link
Copy Markdown
Contributor

@mridulm @srowen sorry not totally following this one. Is there an immediate isolated bug here, or is this a broader issue that we need to add consistency across the code base?

I guess my question is - in what case does the current implementation cause a problem for users?

@vanzin

vanzin commented May 7, 2014

Copy link
Copy Markdown
Contributor

Just my 2 bits, but using getCanonicalPath() is usually a code smell; unless it's explicitly necessary (i.e. you know you have a symlink and you want to remove the actual file it references), it's generally better to use getAbsolutePath().

(Not that I think this applies here, but just commenting on the choice of API.)

@srowen

srowen commented May 7, 2014

Copy link
Copy Markdown
Member

@pwendell @nsuthar

First, note SPARK-1527 (https://issues.apache.org/jira/browse/SPARK-1527) and its PR (#436).

@advancedxy says that was a real issue, and I think it is fixed by the PR, which uses getAbsolutePath(). FWIW I agree with that change.

I think that is slightly preferable to the change in this PR. getCanonicalPath() is probably just fine too, and also solves the issue, but may be unnecessary.

There is a separate issue addressed in this PR / SPARK-1623. That is that the set files, which has Strings, is populated with the result of File.getAbsolutePath() but then later unpopulated with the result of File.toString(), which is File.getPath(), which is not necessarily the same for the same file.

Whether it happens to all work out because the arguments happen to always be absolute, I think it's probably nicer to resolve this by a) calling files.remove(file.getAbsolutePath) or b) just making files a set of File to begin with.

So I suggest:

  • commit the PR for SPARK-1527 and close it
  • abandon this PR and make a new one that implements b) or a) above to resolve SPARK-1623

@advancedxy

Copy link
Copy Markdown
Contributor

Sorry for being late for this conversation. I am busy with my own project.

First, I agree with @srowen. SPARK-1527 was discovered when there were untracked files in my spark git dir after running tests. In DiskBlockManagerSuite, files were created using file.getName as parent folds, which was a relative path to cwd. The cleanup code deletes the original temp dir.

val rootDir0 = Files.createTempDir()
rootDir0.deleteOnExit()
val rootDir1 = Files.createTempDir()
rootDir1.deleteOnExit()
val rootDirs = rootDir0.getName + "," + rootDir1.getName

This is a problem or a bug I prefer because it doesn't do it tends to do.

In SPARK-1527, srowen suggested that toString could return relative path and should use getAbsolutePath instead. After thinking twice, I think it would be ok to use toString if we make sure we don't change working directory between file creation and deletion. That is to say even if tempDir.toString is a relative path, we can still delete this file using file.toString as file path.

Back in this pr, as @srowen said,

the set files, which has Strings, is populated with the result of File.getAbsolutePath() but then later unpopulated with the result of File.toString(), which is File.getPath(), which is not necessarily the same for the same file.

There is a problem. File.getAbsolutePaht is not necessarily the same as File.getPath, or even the same as File.getCanonicalPath
see the code below:

scala>val fstr = "./1.txt"
fstr: String = ./1.txt

scala>val f = new File(fstr)
f: java.io.File = ./1.txt

scala> f.getPath
res0: String = ./1.txt

scala> f.getAbsolutePath
res1: String = /Users/yexianjin/./1.txt

scala> f.getCanonicalPath
res2: String = /Users/yexianjin/1.txt

@mridulm I think you are right, we are at case b)paths relative to cwd and/or what is returned by Files.createTempFile, and I think we should stick to this case. But, we need to review the codebase to make sure path manipulation are correct.

So I suggest(like @srowen said):

  1. choose getPath or getAbsolutePath. getAbsolutePath can deal with changing working directory situation, but getPath or toString is more consistent with spark codebase.
  2. PR for SPARK-1527 could be committed if we use getAbsolutePath, otherwise, we can abandon PR for SPARK-1527 and this PR.
  3. make a new one that implements b) or something similar based on our choice.

what do you think?

@mateiz

mateiz commented Jul 29, 2014

Copy link
Copy Markdown
Contributor

Since we merged in #749 I believe it's okay to close this, right @pwendell ?

@asfgit asfgit closed this in ee91eb8 Aug 27, 2014
whatlulumomo pushed a commit to whatlulumomo/spark_src that referenced this pull request Jul 9, 2019
bzhaoopenstack pushed a commit to bzhaoopenstack/spark that referenced this pull request Sep 11, 2019
Add a walk-around for missing ``started`` time in
case k8s cluster failed to setup. Also copy full
cluster setup logs to logdir for better debuging.

Related-Bug: theopenlab/openlab#257
MaxGekk added a commit to MaxGekk/spark that referenced this pull request Oct 2, 2026
### What changes were proposed in this pull request?

Thirteen open rows of milestone 6 move to milestone 7, on the owner's decision of 2 October 2026 after the review of the milestone's open rows before its close. Each was read against the milestone's done-when list (`PLAN_MILESTONE_6.md` 1.3) and against the size-control rows, the goal set on 30 September, and none bears on either:

- **180**, promotion, continuously: a cadence (the cross-posts, a findings post every two weeks, a living benchmark page) rather than a task the milestone could close; carried, as decided on 1 October.
- **182**, extending Spark's own benchmarks: it was milestone 7's item 8, and returns to it.
- **207 and 208**, two measured micro-optimisations of the range-set kernel.
- **213**, the warm-up choosing which drivers to compile from statistics.
- **214 to 217 and 224**, the rest of the Java port - the time, condition and chrono families, the facade, and the benchmark harness adapter with `VarkaEmitDump` - mechanical, and meant for an agent.
- **218**, optional research into why the loop predicate cycles.
- **222**, structural hashing of IR nodes, a compile-time cost.
- **231**, the fallback path's row loops reading columns through the batch's vectors.

The moves follow the precedent of 15 and 21 September (milestone 5's rows into items 15 and 39): every row keeps its text and number in `PLAN_MILESTONE_6.md` with a "Moved to milestone 7" note at its head, so every citation still resolves; sections 2.8 and 2.9, the design sections of 182 and 180, carry a note under their headings; section 8 lists the thirteen; and `SCOPE_MILESTONE_7.md` item 81 tables them with where each design is and why each left.

Row 181, the second post, published on 1 October, is marked done, the way row 210 was for the first.

What is left open in milestone 6 after this and the pull requests in flight (apache#544 closing 233, apache#546 closing 235, apache#547 closing 223 and adding 239): the two size-control rows, 236 (plan a kernel's size before building it) and 239 (locals a loop method stores and never reads).

### Why are the changes needed?

So that milestone 6 can close on what it set out to do, and the moved work keeps its place and its reasons.

### Does this PR introduce _any_ user-facing change?

No. Two planning documents.

### How was this patch tested?

`dev/varka_quote_check.py` reports zero orphans and `dev/varka_precommit.sh` no findings.

### Was this patch authored or co-authored using generative AI tooling?

Generated-by: Claude Code (Claude Opus 5.5)
MaxGekk added a commit to MaxGekk/spark that referenced this pull request Oct 2, 2026
### What changes were proposed in this pull request?

Task 235, the first of the rows to resolve before milestone 6 closes: the IR fuzzer now draws `NarrowLane`, the root every `TIME` kernel ends in, so its narrowing store is checked against the reference evaluator on random kernels instead of only on the emitter suite's fixed shapes.

**The mechanism.** `NarrowLane` is admitted at an output root only, so it can't be an arm of the grammar's `value` recursion. `LongShapes.root(depth)` draws a value tree as before and, where its magnitude bound fits an int, wraps it in a `NarrowLane` one time in three - only there, because the narrowing truncates with no overflow check, as the compiler builds it only over values proven to fit. `drawLongShape`, `drawWideLongShape` and the long-lane reach test draw their value roots through it. The long-lane differential test reads a narrowing root's output at the store's four bytes a row, sign-extended against the evaluator's 64-bit answer, so a value that did not fit would show as a difference rather than be truncated on both sides.

Since the emitter requires all of a kernel's outputs to share one emission lane, and a narrowing root's is its child's, a drawn kernel can now hold a narrowing root beside plain long roots - the case no fixed shape covered.

**The reach tests.** The long-lane test no longer excludes `NarrowLane`. The int-lane test keeps its exclusion: `NarrowLane` takes a long child, and nothing in the IR widens an int lane, so no int shape can hold one; the reason in the comment now says so.

**What moved with the corpus.** Each shape draws from its own seeded `Random`, so only shapes with a root whose bound fits 32 bits change. Still, the long corpus moves:
- `emitted_bytes.json`: all 100 `fuzz_long` blocks at both widths and the `option_arms` digests; every int-lane block and every curated shape is byte-identical.
- The cost model's price tables (`VarkaEmitCostTable.java`, `VarkaEmitCostRegister.java`) are fitted over that corpus, so they are refitted, and `emit_cost_audit.json` regenerated after them. The refit moves no default emission: `emitted_bytes.json`, regenerated under the old prices, passes unchanged under the new ones.
- `VarkaEmitCostSuite`'s pinned list of wide shapes that gain a loop method under `predictGrouping` (off by default) names new shapes.

**Results** (`PLAN_TASK_235.md` 9):
- 455 of the first 10,000 long shapes carry a narrowing root, 379 of them beside a plain root; the long-lane fuzz finds no mismatch at 300 iterations or at 10,000.
- No conclusion of `PLAN_TASK_199.md` 9 moves: the fitted model's error at 2000 bytes and over is still 2.2% at the median and 12.4% at the 99th percentile.
- Two findings for task 236's planner: at 8000 bytes and over the fitted model under-predicts 94.7% of methods, by at most 4.9%, so a planner trusting it near the budget needs that margin; and two of the new pinned shapes gain their loop method by a prediction erring high - the weights build once, yet the prediction closes a group early - where the recorded case is a prediction erring low.

Row 235 is marked done in `PLAN_MILESTONE_6.md`.

### Why are the changes needed?

A fuzz run says nothing about a node the generator never builds, and `NarrowLane` is in every `TIME` kernel.

### Does this PR introduce _any_ user-facing change?

No. Test code, regenerated test oracles and price tables, and the plans; no default emission changes.

### How was this patch tested?

`VarkaIrFuzzSuite` at its defaults and the long-lane test at 10,000 iterations; every other consumer of the long-lane draw and the cost corpus - `VarkaEmittedBytesSuite`, `VarkaEmitCostAuditSuite`, `VarkaEmitCostSuite`, `VarkaEmitterDriverTableSuite`, `VarkaEmitterBudgetSuite`, `VarkaExactGroupingSuite`, `VarkaGroupingBoundSuite`, `VarkaLaneTypeSuite`, `VarkaRangeAnalysisSuite` - 92 tests passed, one opt-in test canceled as designed. `dev/varka_quote_check.py` reports zero orphans and `dev/varka_precommit.sh` no findings.

### Was this patch authored or co-authored using generative AI tooling?

Generated-by: Claude Code (Claude Opus 5.5)
MaxGekk pushed a commit to MaxGekk/spark that referenced this pull request Oct 2, 2026
Brings apache#546 (task 235: the long draws take a NarrowLane root). Its refit and this branch's both regenerated VarkaEmitCostTable.java, emit_cost_audit.json and emitted_bytes.json, so the three conflicted; each is regenerated on the merged tree (VARKA_COST_REGEN on VarkaEmitCostSuite, then on VarkaEmitCostAuditSuite, then VARKA_BYTES_REGEN), and the cost, audit, bytes, arithmetic, driver-table, budget, fuzz and grouping-bound suites pass against them. The register is unchanged by the refit. PLAN_TASK_223.md and row 223 now quote the merged audit: 155 methods of 8000 bytes and over against master's 169, where they said 139 against 161; section 9.1 says its long-lane rows were counted on the corpus before task 235.

Generated-by: Claude Code (Claude Opus 5.5)
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.

10 participants