Make export usable on large accounts (10k SHOW cap, O(n^2) grant lookup, unqualified resources) - #47
Draft
jacodegroothydrab wants to merge 2 commits into
Draft
Conversation
…nt lookup) On a large account (~15k tables, ~90k grants) `snowcap export --all` does not complete. Three independent causes: 1. `SHOW ... IN ACCOUNT` is capped at 10,000 rows by Snowflake (error 090153), so list_tables / list_views / list_stages fail outright once an account exceeds that. They now read SNOWFLAKE.ACCOUNT_USAGE, falling back to SHOW when ACCOUNT_USAGE is unavailable or errors. Trade-off: ACCOUNT_USAGE lags live state (commonly up to ~2h), so very recently created objects can be missed. On an account past the cap a slightly stale answer seemed better than no answer, but I am happy to put this behind an explicit flag instead if you would prefer that. 2. `_show_grants_to_role` re-filtered the entire cached ACCOUNT_USAGE grant list on every call. A 300-grant profile showed 32.7M `str.upper()` calls. Now indexed by grantee once per session. 3. `_fetch_grant_to_role` scanned a role's whole grant list and constructed two ResourceName objects per candidate. Now indexed by (granted_on, privilege, name). ResourceName cannot key a dict directly: __hash__ is hash(str(self)), but __eq__ treats quoted "FOO" and unquoted FOO as equal while str() keeps the quotes, so the two disagree. _grant_name_key normalises to "exact if quoted, upper-cased otherwise", which reproduces __eq__ across all four quoted/unquoted combinations. Both indexes are invalidated in reset_account_usage_caches alongside the cache they derive from. Also exposes --threads (default raised 4 -> 16) and --use-account-usage on export; neither was reachable from the CLI. Threads help the IO-bound resource types, but the grant phase is CPU-bound and GIL-serialised, so indexing was the only thing that could address it. Measured on the affected account, 5,000-grant sample, steady state: before 48.32 ms/grant -> ~74 min projected for the grant phase alone after 0.01 ms/grant -> ~1 second Full `export --all --exclude=table,view`: did not complete -> 125 seconds.
export omits database/schema from schema-scoped resources (file formats,
stages, procedures, ...) even though the URN already carries them. Two
consequences:
- The output is ambiguous when two schemas contain the same object name. No
post-processing keyed on the bare name can tell such resources apart.
- plan/apply reject unqualified resources once a config spans more than one
database ("Resource FILE FORMAT 'X' has no schema"), so an --all export of a
multi-database account cannot be fed back in without hand-editing.
_format_resource_config now emits the location taken from the URN, alongside
the existing ResourceType.SCHEMA case.
Contributor
|
The ACCOUNT_USAGE approach is the right call here — on large accounts SHOW hits the 10k cap and truncates, so this is the only thing that actually works at scale, and nice job filtering
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Opening as a draft to demonstrate the problem. This is all Claude generated, and I would recommend to use this PR as inspiration only. The main problem is the 10K limit of SHOW. Beyond that Claude identified an O(n^2) grant lookup. In my case it reduced the import time from > 75 minutes (it never finished) to a few minutes. Because of the slow running it first tried to increase the threads from 4 to 16, that change wasn't needed in the end.
On a large account (~15k tables, ~90k grants)
snowcap export --alldoes not complete. Digging into it turned up three independent problems.1.
SHOW ... IN ACCOUNTis capped at 10,000 rowslist_tables,list_viewsandlist_stagesall useSHOW ... IN ACCOUNT, which Snowflake caps at 10,000 rows:Past that, listing fails outright and takes
plan/exportwith it. They now readSNOWFLAKE.ACCOUNT_USAGE, falling back toSHOWwhen it is unavailable or errors.Trade-off, and the bit I would most like your view on:
ACCOUNT_USAGElags live state (commonly up to ~2h), so a very recently created object can be missed. I made it the default on the reasoning that on an account past the cap a slightly stale answer beats no answer at all — but if you would rather it sat behind an explicit flag, or only engaged as a fallback after a 090153, I am glad to rework it.2.
_show_grants_to_rolerescanned the whole grant cache per callIt re-filtered the entire cached
ACCOUNT_USAGEgrant list on every call. A 300-grant profile showed 32.7Mstr.upper()calls. Now indexed by grantee, once per session.3.
_fetch_grant_to_rolescanned a role's grants linearlyIt walked the role's full grant list, constructing two
ResourceNameobjects per candidate. Now indexed by(granted_on, privilege, name).ResourceNamecan't key a dict directly:__hash__ishash(str(self)), but__eq__treats quoted"FOO"and unquotedFOOas equal whilestr()keeps the quotes — so hash and equality disagree._grant_name_keynormalises to "exact if quoted, upper-cased otherwise", which reproduces__eq__across all four quoted/unquoted combinations.Both indexes are invalidated in
reset_account_usage_cachesalongside the cache they derive from.Results
Measured on the affected account, 5,000-grant sample, steady state:
export --all --exclude=table,view: did not complete → 125 seconds.Also included
--threadsonexport(default raised 4 → 16) and--use-account-usage— neither was reachable from the CLI. Threads help the IO-bound resource types; the grant phase is CPU-bound and GIL-serialised, so only indexing addressed it.exportomitsdatabase/schemafrom file formats, stages, procedures etc. even though the URN carries them. That makes output ambiguous when two schemas share an object name, andplan/applyreject unqualified resources once a config spans multiple databases (Resource FILE FORMAT 'X' has no schema) — so an--allexport of a multi-database account can't be fed back in without hand-editing. Happy to split this into its own PR if you'd prefer.Not done
No tests added yet — I wanted to check the direction first, especially on the
ACCOUNT_USAGEdefault. Glad to add coverage for the two index helpers and the qualification change if the approach looks right.