Skip to content
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

Add a version of TryGetRecentCheckResultsForFile with project snapshot #16720

Merged

Conversation

dawedawe
Copy link
Contributor

@dawedawe dawedawe commented Feb 15, 2024

Description

As the title says, this adds a version of TryGetRecentCheckResultsForFile that works with a project snapshot.

Checklist

  • Test cases added

  • Performance benchmarks added in case of performance changes

  • Release notes entry updated:

    Please make sure to add an entry with short succinct description of the change as well as link to this pull request to the respective release notes file, if applicable.

    Release notes files:

    • If anything under src/Compiler has been changed, please make sure to make an entry in docs/release-notes/.FSharp.Compiler.Service/<version>.md, where <version> is usually "highest" one, e.g. 42.8.200
    • If language feature was added (i.e. LanguageFeatures.fsi was changed), please add it to docs/releae-notes/.Language/preview.md
    • If a change to FSharp.Core was made, please make sure to edit docs/release-notes/.FSharp.Core/<version>.md where version is "highest" one, e.g. 8.0.200.

    Information about the release notes entries format can be found in the documentation.
    Example:

    If you believe that release notes are not necessary for this PR, please add NO_RELEASE_NOTES label to the pull request.

…t takes a snapshot instead of project options
Copy link
Contributor

github-actions bot commented Feb 15, 2024

⚠️ Release notes required, but author opted out

Warning

Author opted out of release notes, check is disabled for this pull request.
cc @dotnet/fsharp-team-msft

Copy link
Contributor

@0101 0101 left a comment

Choose a reason for hiding this comment

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

I think for this method we should try to use the cache we already have with the results instead of creating a new one.

Maybe we can extend the version of the ParseAndCheckFileInProject to include file hash and add a method to retrieve a value based on key and version -> bool predicate.

That way we would have the same behavior, if I understand it correctly, where we care about the file's contents + ProjectCore matching, but other files' contents could be different.

…ForFile

- extend version of ParseAndCheckFileInProject cache with the check sum of the source code
- add test
@dawedawe
Copy link
Contributor Author

Hey @0101 ,
thanks a lot for the review.

Maybe we can extend the version of the ParseAndCheckFileInProject to include file hash and add a method to retrieve a value based on key and version -> bool predicate.

I extended the version of the ParseAndCheckFileInProject cache to include the hash of the source string.

Did I understand you correctly that we should add a new method to the AsyncMemoize type taking a key and a version predicate? As you can see, I'm not entirely sure what you had in mind there or how I should use the API, thus the dummyVersion to be able to call cache.GetAll

@0101
Copy link
Contributor

0101 commented Feb 17, 2024

Hey @dawedawe

Did I understand you correctly that we should add a new method to the AsyncMemoize type taking a key and a version predicate? As you can see, I'm not entirely sure what you had in mind there or how I should use the API, thus the dummyVersion to be able to call cache.GetAll

I have not necessarily thought it all through... But we shouldn't need dummyVersion :) I'd suggest adding

LruCache.GetAll: 'TKey -> ('TVersion * 'TValue) list

And make it return the strongly held value (if present) first, so that in case there are multiple items matching we'll pick that one (which would be most recent). Don't know if we can give any meaningful ordering besides that. If we really wanted we could maybe add a timestamp or a counter to JobCompleted.

Possibly we can then implement the existing GetAll: key: 'TKey * version: 'TVersion -> 'TValue option * ('TVersion * 'TValue) list using the new one to reduce duplication. (Or maybe we could even remove it, because it's only use if we have AsyncMemoize(cancelDuplicateRunningJobs = true) which we never use.)

@dawedawe dawedawe marked this pull request as ready for review February 19, 2024 13:25
@dawedawe dawedawe requested a review from a team as a code owner February 19, 2024 13:26
@nojaf nojaf added the NO_RELEASE_NOTES Label for pull requests which signals, that user opted-out of providing release notes label Feb 19, 2024
Co-authored-by: Petr Pokorny <petr@innit.cz>
@0101
Copy link
Contributor

0101 commented Feb 19, 2024

Looking at the original docstring:

/// <summary>
/// Try to get type check results for a file. This looks up the results of recent type checks of the
/// same file, regardless of contents. The version tag specified in the original check of the file is returned.
/// If the source of the file has changed the results returned by this function may be out of date, though may
/// still be usable for generating intellisense menus and information.
/// </summary>
///
/// <param name="fileName">The file name for the file.</param>
/// <param name="options">The options for the project or script, used to determine active --define conditionals and other options relevant to parsing.</param>
/// <param name="sourceText">Optionally, specify source that must match the previous parse precisely.</param>
/// <param name="userOpName">An optional string used for tracing compiler operations associated with this request.</param>

It says the sourceText is optional, and if not given we should return any result for that filename. Although BackgroundCompiler currently just returns None if sourceText is not given 🤷

match sourceText with
| Some sourceText ->
let hash = sourceText.GetHashCode() |> int64
let resOpt =
parseCacheLock.AcquireLock(fun ltok -> checkFileInProjectCache.TryGet(ltok, (fileName, hash, options)))
match resOpt with
| Some res ->
match res.TryPeekValue() with
| ValueSome(a, b, c, _) -> Some(a, b, c)
| ValueNone -> None
| None -> None
| None -> None

Not sure what we should do for the new API. Probably we don't have to worry about this behavior since no one can be relying on it now when it doesn't work... We could make it work with an extra parameter (optional version of the file), but also I'm not sure how useful are old diagnostics when the source has changed.

@dawedawe
Copy link
Contributor Author

Looking at the original docstring:

/// <summary>
/// Try to get type check results for a file. This looks up the results of recent type checks of the
/// same file, regardless of contents. The version tag specified in the original check of the file is returned.
/// If the source of the file has changed the results returned by this function may be out of date, though may
/// still be usable for generating intellisense menus and information.
/// </summary>
///
/// <param name="fileName">The file name for the file.</param>
/// <param name="options">The options for the project or script, used to determine active --define conditionals and other options relevant to parsing.</param>
/// <param name="sourceText">Optionally, specify source that must match the previous parse precisely.</param>
/// <param name="userOpName">An optional string used for tracing compiler operations associated with this request.</param>

It says the sourceText is optional, and if not given we should return any result for that filename. Although BackgroundCompiler currently just returns None if sourceText is not given 🤷

match sourceText with
| Some sourceText ->
let hash = sourceText.GetHashCode() |> int64
let resOpt =
parseCacheLock.AcquireLock(fun ltok -> checkFileInProjectCache.TryGet(ltok, (fileName, hash, options)))
match resOpt with
| Some res ->
match res.TryPeekValue() with
| ValueSome(a, b, c, _) -> Some(a, b, c)
| ValueNone -> None
| None -> None
| None -> None

Not sure what we should do for the new API. Probably we don't have to worry about this behavior since no one can be relying on it now when it doesn't work... We could make it work with an extra parameter (optional version of the file), but also I'm not sure how useful are old diagnostics when the source has changed.

I also think we don't have to worry but from a conversation last week, I know that editor developers were confused about the behaviour regarding returning always None when no sourceText was given.

- let tests run for background compiler, too
Co-authored-by: Petr Pokorny <petr@innit.cz>
dawedawe and others added 3 commits February 22, 2024 00:16
…Compiler.fs

Co-authored-by: Petr Pokorny <petr@innit.cz>
…Compiler.fs

Co-authored-by: Petr Pokorny <petr@innit.cz>
Copy link
Contributor

@0101 0101 left a comment

Choose a reason for hiding this comment

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

Looks good! Sorry it was so difficult 😅 I'd be happy for any suggestions on how to make working with the code easier. Probably some things should be renamed but I don't know to what... Maybe some more documentation?

@dawedawe
Copy link
Contributor Author

Looks good! Sorry it was so difficult 😅 I'd be happy for any suggestions on how to make working with the code easier. Probably some things should be renamed but I don't know to what... Maybe some more documentation?

Ah, don't take me as a benchmark, I was pretty dump this week.
But yes, some documentation about the cache design, how keys and their versions work would be great.

Thanks again for all the hand-holding here! I can't wait to have all this in the IDEs.

@psfinaki
Copy link
Member

/azp run

Copy link

Azure Pipelines successfully started running 2 pipeline(s).

Copy link
Member

@psfinaki psfinaki left a comment

Choose a reason for hiding this comment

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

Good stuff. Thanks for your enduring help with TC :)

@psfinaki psfinaki enabled auto-merge (squash) February 23, 2024 16:51
@dawedawe
Copy link
Contributor Author

Mmh, no idea why the one test failed in the last run. I can't reproduce that locally 🤷
Can this be related to something else?

@psfinaki psfinaki merged commit db6b8cd into dotnet:main Feb 23, 2024
29 checks passed
@dawedawe dawedawe deleted the add_trygetrecentcheckresultsforfile_with_snapshot branch February 23, 2024 19:00
@psfinaki
Copy link
Member

Yeah sorry we have a lot flaky tests recently.

@0101
Copy link
Contributor

0101 commented Feb 26, 2024

Yeah sorry we have a lot flaky tests recently.

But that was a test added by this PR 😂. We might have to revisit it so we don't increase the flakiness.

The tests in the CI sometimes behave quite unpredictably compared to local runs (when working with Tasks/async stuff), so these failures are not reproducible. I think it's when the CI machine is overloaded or something and tasks that are started don't actually start running for a long time. So some barriers are needed before we assert something.

But also maybe this test has been fully replaced by the new workflow tests and can just be removed?

@dawedawe
Copy link
Contributor Author

But also maybe this test has been fully replaced by the new workflow tests and can just be removed?

Yep, let's remove it.

nojaf pushed a commit to nojaf/fsharp that referenced this pull request Feb 28, 2024
dotnet#16720)

* First stab at adding a version of TryGetRecentCheckResultsForFile that takes a snapshot instead of project options

* use version type without defaultof<_> = null

* take sourcetext out of project snapshot, kind of forces us to an async return but let's see if this is acceptable

* format

* - reuse ParseAndCheckFileInProject cache for TryGetRecentCheckResultsForFile
- extend version of ParseAndCheckFileInProject cache with the check sum of the source code
- add test

* format

* cleanup

* use a new LruCache member GetAll to get rid of the dummy version

* cleanup

* Update src/Compiler/Service/TransparentCompiler.fs

Co-authored-by: Petr Pokorny <petr@innit.cz>

* unify key creation

* just use ProjectSnapShot.FileKey

* to have it on record, push a version with "f.Version |> Md5Hasher.toString" as the second part of lastFileKey.Version

* use FileKeyWithExtraFileSnapshotVersion for the ParseAndCheckFileInProject cache

* replace FileSnapShot after edit in Test

* add CustomOperation tryGetRecentCheckResults for tests

* - Make API non-async and don't return hash
- let tests run for background compiler, too

* better fix for commandLineOptions order

* Update src/Compiler/Service/FSharpProjectSnapshot.fs

Co-authored-by: Petr Pokorny <petr@innit.cz>

* Update tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs

Co-authored-by: Petr Pokorny <petr@innit.cz>

* Update tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs

Co-authored-by: Petr Pokorny <petr@innit.cz>

* fix version predicate

* let LruCache.GetAll(key: 'TKey) return a seq instead of a list

* compare signatures in CustomOperation tryGetRecentCheckResults to tighten the tests

---------

Co-authored-by: Petr Pokorny <petr@innit.cz>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NO_RELEASE_NOTES Label for pull requests which signals, that user opted-out of providing release notes
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

4 participants