Skip to content

Spark 4.1: Add rewrite option to enable executor cache for delete files - #17868

Open
anuragmantri wants to merge 2 commits into
apache:mainfrom
anuragmantri:rewrite-deletes-cache-option
Open

Spark 4.1: Add rewrite option to enable executor cache for delete files#17868
anuragmantri wants to merge 2 commits into
apache:mainfrom
anuragmantri:rewrite-deletes-cache-option

Conversation

@anuragmantri

Copy link
Copy Markdown
Collaborator

Related to #11648.

RewriteDataFilesSparkAction has disabled the executor cache for delete files unconditionally since #13820. As noted in the discussion on #11648, that is the right default for position deletes but costly for equality deletes, which cannot be narrowed to a single data file while reading and are therefore re-read in full for every data file whose bounds overlap.

This adds a rewrite option so users can opt back in, where a Spark executor cache setting is exposed as a rewrite option rather than a session property.

Existing Default behavior is unchanged.

Usage from SQL:

CALL catalog_name.system.rewrite_data_files(
  table => 'db.sample',
  options => map('executor-cache.delete-files.enabled', 'true')
);

Usage from the action API:

SparkActions.get(spark)
    .rewriteDataFiles(table)
    .option(RewriteDataFilesSparkAction.EXECUTOR_CACHE_DELETE_FILES_ENABLED, "true")
    .execute();

Tests added to TestSparkExecutorCache assert the number of times each delete file is opened during a rewrite: 2 per delete file with the option at its default, once per data file, and 1 with the option enabled.


AI Disclosure

  • Model: Claude Opus 5
  • Platform/Tool: Claude Code
  • Human Oversight: fully reviewed by me after the changes were made.
  • Prompt Summary: Make the executor cache optional during RewriteDataFiles via a new rewrite option, keeping the existing default, then review the change against AGENTS.md and add tests.

@anuragmantri

Copy link
Copy Markdown
Collaborator Author

@RussellSpitzer @kinolaev - Could you take a look? Thanks!

Comment thread docs/docs/spark-procedures.md Outdated
| `output-spec-id` | current partition spec id | Identifier of the output partition spec. Data will be reorganized during the rewrite to align with the output partitioning. |
| `remove-dangling-deletes` | false | Remove dangling position and equality deletes after rewriting. A delete file is considered dangling if it does not apply to any live data files. Enabling this will generate an additional commit for the removal. |
| `max-files-to-rewrite` | null | This option sets an upper limit on the number of eligible files that will be rewritten. If this option is not specified, all eligible files will be rewritten. |
| `executor-cache.delete-files.enabled` | false | Use the executor cache for delete files while rewriting. Enable this when the same delete file applies to many data files, which is common with equality deletes |

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.

let's try to keep this to one snake case thing.
cache-delete-files?

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.

Given that the Spark property is called spark.sql.iceberg.executor-cache.enabled and we already have the partial-progress.enabled option on this procedure, my preference would be executor-cache.enabled.

@RussellSpitzer RussellSpitzer Aug 28, 2026

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.

I'd rather we not use a "." property if we can help. The partial progress ones are not a good example based on all the other procedure options we have. All the others use the kabob thing and I think it's because only partial progress was originally defined in the "action".

enable-executor-cache is also fine, but I don't think it really explains what the option is doing. It only effects deletes and specifically delete files so I'd try to keep the name tied to that functionality

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I kept it to cache-delete-files. This is concise and conveys the intention.

*
* <p>Defaults to false.
*/
public static final String EXECUTOR_CACHE_DELETE_FILES_ENABLED =

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.

As I mentioned above, lets just drop the other parts here, I think just CACHE_DELETE_FILES is fine

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done.

public void testExecutorCacheForDeleteFilesDisabled() {
Table table = createTablePartitioned(1, 1);
RewriteDataFilesSparkAction action = SparkActions.get(spark).rewriteDataFiles(table);
action.execute();

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.

?

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.

Ok I can see this is because we moved it into "validate"

Since we are in the same package can we just call validate rather than actually executing?

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.

Ok we can't do that. Maybe we should move this back to the constructor?

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.

We can't, because.option() is called after the constructor

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.

That's too bad. i'm trying to figure out how we can avoid running 3 additional spark rewrites in the tests here when all we really care about is the conf being on or off.

@anuragmantri anuragmantri Aug 28, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Okay, I looked at all the other options tests. All test behavior. This one is different. IMO, we have covered the behavior tests in TestSparkExecutorCache so we probably don't need these tests here. I removed them. Let me know if you feel otherwise.

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.

Can we make the init method package-private? I checked, and the removed tests pass when calling init(0L) instead of execute.
I am also fine with the current tests in TestSparkExecutorCache.

@kinolaev

Copy link
Copy Markdown
Contributor

LGTM. Although I prefer a slightly shorter name (see above), the current option is also fine with me. Thanks @anuragmantri for the PR!

@anuragmantri anuragmantri left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for the quick reviews.

I also thought about the three options we have. It can be confusing but I kept all three. My rationale is that these serve slightly different purposes.

  1. executor-cache.enabled - Main switch to control caching, also gates locality. Predates my PRs to add ability to disable delete files only.
  2. executor-cache.delete-files.enabled - A scan option narrowed down for delete files cache onl.
  3. cache-delete-files this is a rewrite option that controls 2 for scans during rewrites.

Comment thread docs/docs/spark-procedures.md Outdated
| `output-spec-id` | current partition spec id | Identifier of the output partition spec. Data will be reorganized during the rewrite to align with the output partitioning. |
| `remove-dangling-deletes` | false | Remove dangling position and equality deletes after rewriting. A delete file is considered dangling if it does not apply to any live data files. Enabling this will generate an additional commit for the removal. |
| `max-files-to-rewrite` | null | This option sets an upper limit on the number of eligible files that will be rewritten. If this option is not specified, all eligible files will be rewritten. |
| `executor-cache.delete-files.enabled` | false | Use the executor cache for delete files while rewriting. Enable this when the same delete file applies to many data files, which is common with equality deletes |

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I kept it to cache-delete-files. This is concise and conveys the intention.

*
* <p>Defaults to false.
*/
public static final String EXECUTOR_CACHE_DELETE_FILES_ENABLED =

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done.

public void testExecutorCacheForDeleteFilesDisabled() {
Table table = createTablePartitioned(1, 1);
RewriteDataFilesSparkAction action = SparkActions.get(spark).rewriteDataFiles(table);
action.execute();

@anuragmantri anuragmantri Aug 28, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Okay, I looked at all the other options tests. All test behavior. This one is different. IMO, we have covered the behavior tests in TestSparkExecutorCache so we probably don't need these tests here. I removed them. Let me know if you feel otherwise.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants