-
-
Notifications
You must be signed in to change notification settings - Fork 785
System resource cache #5289
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: develop
Are you sure you want to change the base?
System resource cache #5289
Changes from all commits
35f63cc
c54d124
212d559
e4fde9a
0916e64
d69dfc7
fc0856a
866309a
7988a13
fa06544
5af2be0
630074d
60ee685
ab1932a
1247f39
eb13ccb
7871563
fd761e0
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 |
|---|---|---|
|
|
@@ -67,6 +67,7 @@ class WindowsError(Exception): | |
| from scancode import notice | ||
| from scancode import print_about | ||
| from scancode import Scanner | ||
| from scancode import results_cache | ||
| from scancode.help import epilog_text | ||
| from scancode.help import examples_text | ||
| from scancode.interrupt import DEFAULT_TIMEOUT | ||
|
|
@@ -407,12 +408,18 @@ def default_processes(): | |
| help_group=cliutils.MISC_GROUP, sort_order=1000, cls=PluggableCommandLineOption) | ||
|
|
||
| @click.option( | ||
| "--check-version/--no-check-version", | ||
| help="Whether to check for new versions. Defaults to true.", | ||
| '--check-version/--no-check-version', | ||
| help='Whether to check for new versions. Defaults to true.', | ||
| default=True, | ||
| # not yet supported in Click 6.7 but added in PluggableCommandLineOption | ||
| hidden=True, | ||
| help_group=cliutils.MISC_GROUP, sort_order=1000, cls=PluggableCommandLineOption) | ||
|
|
||
| @click.option('--use-cached-results', | ||
|
Member
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. Was just wondering if this is the best name since this option enables both the creation of the cache and the use of cached results and this name only indicates the latter. Alternatives could be:
Member
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. One other option is to use two other options one to create the cache and the other to use the cached results, but using just one option is probably much better if there are no large performance implications.
Member
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. I wasn't sure about this too. My original thought was to update the cache in all circumstances, even if we are not using it to get scan results, but I decided against it because this is still an experimental feature and maybe it wouldn't be a good idea to create a whole bunch of cache files that wouldn't normally be used. |
||
| is_flag=True, | ||
| default=False, | ||
| help='(EXPERIMENTAL) Enables the creation, update, and usage of cached results during scan time.', | ||
| help_group=cliutils.CORE_GROUP, sort_order=250, cls=PluggableCommandLineOption) | ||
| def scancode( | ||
| ctx, | ||
| input, # NOQA | ||
|
|
@@ -433,6 +440,7 @@ def scancode( | |
| test_error_mode, | ||
| keep_temp_files, | ||
| check_version, | ||
| use_cached_results, | ||
| echo_func=echo_stderr, | ||
| *args, | ||
| **kwargs, | ||
|
|
@@ -549,6 +557,7 @@ def scancode( | |
| return_results=False, | ||
| echo_func=echo_func, | ||
| outdated=outdated, | ||
| use_cached_results=use_cached_results, | ||
| *args, | ||
| **kwargs | ||
| ) | ||
|
|
@@ -570,7 +579,7 @@ def scancode( | |
|
|
||
|
|
||
| def run_scan( | ||
| input, # | ||
| input, # | ||
| config_file=None, | ||
| ignore=[], | ||
| from_json=False, | ||
|
|
@@ -594,6 +603,7 @@ def run_scan( | |
| pretty_params=None, | ||
| plugin_options=plugin_options, | ||
| outdated=None, | ||
| use_cached_results=False, | ||
| *args, | ||
| **kwargs | ||
| ): | ||
|
|
@@ -1006,6 +1016,7 @@ def echo_func(*_args, **_kwargs): | |
| verbose=verbose, | ||
| kwargs=requested_options, | ||
| echo_func=echo_func, | ||
| use_cached_results=use_cached_results, | ||
| ) | ||
| success = success and scan_success | ||
|
|
||
|
|
@@ -1141,7 +1152,7 @@ def load_configuration_file(path): | |
|
|
||
| click.echo(f"Loading env from {path}") | ||
| try: | ||
|
|
||
| config_values = saneyaml.load(path.read()) | ||
| ignores = config_values.get("ignored_patterns", []) | ||
| except (saneyaml.YAMLError, Exception): | ||
|
|
@@ -1226,6 +1237,7 @@ def run_scanners( | |
| verbose=False, | ||
| kwargs=None, | ||
| echo_func=echo_stderr, | ||
| use_cached_results=False, | ||
| ): | ||
| """ | ||
| Run the list of `stage` ScanPlugin `plugins` on `codebase`. | ||
|
|
@@ -1267,7 +1279,8 @@ def run_scanners( | |
| # TODO: add CLI option to bypass cache entirely? | ||
| scan_success = scan_codebase( | ||
| codebase, scanners, processes, timeout, | ||
| with_timing=timing, progress_manager=progress_manager) | ||
| with_timing=timing, progress_manager=progress_manager, | ||
| use_cached_results=use_cached_results) | ||
|
|
||
| # TODO: add progress indicator | ||
| # run the process codebase of each scan plugin (most often a no-op) | ||
|
|
@@ -1305,6 +1318,7 @@ def scan_codebase( | |
| with_timing=False, | ||
| progress_manager=None, | ||
| echo_func=echo_stderr, | ||
| use_cached_results=False, | ||
| ): | ||
| """ | ||
| Run the `scanners` Scanner objects on the `codebase` Codebase. Return True | ||
|
|
@@ -1326,15 +1340,16 @@ def scan_codebase( | |
| """ | ||
|
|
||
| # NOTE: we never scan directories | ||
| resources = ((r.location, r.path) for r in codebase.walk() if r.is_file) | ||
| resources = ((r.location, r.path, r.name) for r in codebase.walk() if r.is_file) | ||
|
|
||
| use_threading = processes >= 0 | ||
| runner = partial( | ||
| scan_resource, | ||
| scanners=scanners, | ||
| timeout=timeout, | ||
| with_timing=with_timing, | ||
| with_threading=use_threading | ||
| with_threading=use_threading, | ||
| use_cached_results=use_cached_results, | ||
| ) | ||
|
|
||
| if TRACE: | ||
|
|
@@ -1462,11 +1477,12 @@ def terminate_pool_with_backoff(pool, number_of_trials=3): | |
|
|
||
|
|
||
| def scan_resource( | ||
| location_path, | ||
| location_path_name, | ||
| scanners, | ||
| timeout=DEFAULT_TIMEOUT, | ||
| with_timing=False, | ||
| with_threading=True, | ||
| use_cached_results=False, | ||
| ): | ||
| """ | ||
| Given a ``location_path`` tuple pf (location, path), return a tuple of: | ||
|
|
@@ -1489,10 +1505,11 @@ def scan_resource( | |
| processing and threading works. | ||
| """ | ||
| scan_time = time() | ||
| location, path = location_path | ||
| location, path, name = location_path_name | ||
| results = {} | ||
| scan_errors = [] | ||
| timings = {} if with_timing else None | ||
| scanners_to_run = [] | ||
|
|
||
| if not with_threading: | ||
| interruptor = fake_interruptible | ||
|
|
@@ -1503,8 +1520,28 @@ def scan_resource( | |
| # and start returning values. The kill timeout is otherwise there | ||
| # as a gatekeeper for runaway processes. | ||
|
|
||
| results_cache_index = '' | ||
| if use_cached_results: | ||
| # compute results_cache_index | ||
| results_cache_index = results_cache.compute_results_cache_index(location=location, filename=name) | ||
|
|
||
| # update `results` with cached data or add scanner to scanners_to_run if no | ||
| # cache data is available | ||
| for scanner in scanners: | ||
| # get resource_cache_data | ||
| resource_cache_data = results_cache.get_results_cache_data( | ||
| results_cache_index=results_cache_index, | ||
| plugin_name=scanner.name | ||
| ) | ||
| if resource_cache_data: | ||
| results.update(resource_cache_data) | ||
| else: | ||
| scanners_to_run.append(scanner) | ||
| else: | ||
| scanners_to_run = scanners | ||
|
|
||
| # run each scanner in sequence in its own interruptible | ||
| for scanner in scanners: | ||
| for scanner in scanners_to_run: | ||
| if with_timing: | ||
| start = time() | ||
|
|
||
|
|
@@ -1523,6 +1560,12 @@ def scan_resource( | |
| # the return value of a scanner fun MUST be a mapping | ||
| if values_mapping: | ||
| results.update(values_mapping) | ||
| if use_cached_results: | ||
| results_cache.update_results_cache_data( | ||
| results_cache_index=results_cache_index, | ||
| plugin_name=scanner.name, | ||
| results=values_mapping, | ||
| ) | ||
|
|
||
| except Exception: | ||
| msg = 'ERROR: for scanner: ' + scanner.name + ':\n' + traceback.format_exc() | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we include some functionality to re-compute the cache for a file based on data structure changes caused by different versions of scancode, or different CLI options used with different output data formats? There is some discussion on this at #3941
We need to check that we atleast handle the errors resulting out of this. Additionally it would be nice to somehow include the data format versions in the index cache too, smartly avoiding cache recompute whenever possible.
Output data formats are not always changes based on scancode versions, and an update in the output data format version also doesn't correspond to updates in all the scanner output data. So we need to probably compute a hash based on all the data fields present for a particular scanner data based on the keys present in the mapping and include this in the index file path? This would cover both variations caused by scancode version changes and CLI options used.
This is an enhancement though, so feel free to mark this out of scope.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm avoiding the issue of the cache being different between scancode versions because of scanner output by having a separate cache for each version of scancode. I feel like a cache migration tool can get complex quickly, but I also feel that we may want to bring over an existing large cache and something like this would be useful for that.