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

Improve UI responsiveness when doing node search #15773

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

pinzart90
Copy link
Contributor

@pinzart90 pinzart90 commented Jan 22, 2025

Purpose

Run the canvas search in a separate thread to improve UI responsiveness.
This PR was created as an alternative to #15733

Summary of changes:
Moved the Lucene IndexSearcher initialization. Previously it was recreated on every search. Now it is created only after the index is modified.
Run in canvas search in a separate thread (thread pool)
Introduced a cancellation token throughout the search operations
New searches will cancel old ones and only update the UI if the search was successful(not cancelled)
These changes will be under a new feature flag

Left to do:
Add tests
Add to library view search and others (pm search, autocomplete )?

Declarations

Check these if you believe they are true

  • The codebase is in a better state after this PR
  • Is documented according to the standards
  • The level of testing this PR includes is appropriate
  • User facing strings, if any, are extracted into *.resx files
  • All tests pass using the self-service CI.
  • Snapshot of UI changes, if any.
  • Changes to the API follow Semantic Versioning and are documented in the API Changes document.
  • This PR modifies some build requirements and the readme is updated
  • This PR contains no files larger than 50 MB

Release Notes

(FILL ME IN) Brief description of the fix / enhancement. Use N/A to indicate that the changes in this pull request do not apply to Release Notes. Mandatory section

Reviewers

(FILL ME IN) Reviewer 1 (If possible, assign the Reviewer for the PR)

(FILL ME IN, optional) Any additional notes to reviewers or testers.

FYIs

(FILL ME IN, Optional) Names of anyone else you wish to be notified of

dirReader = writer != null ? writer.GetReader(applyAllDeletes: true) : DirectoryReader.Open(indexDir);
Searcher = new(dirReader);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@RobertGlobant20
I opted to initialize the Searcher (and dirReader) after every index modification that I could find.
Do you think I got them all ?

Copy link
Contributor

Choose a reason for hiding this comment

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

yes, I think all of them were covered.

Copy link

UI Smoke Tests

Test: success. 11 passed, 0 failed.
TestComplete Test Result
Workflow Run: UI Smoke Tests
Check: UI Smoke Tests

Copy link
Contributor

@RobertGlobant20 RobertGlobant20 left a comment

Choose a reason for hiding this comment

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

LGTM with one comment

@@ -627,6 +641,7 @@ internal void AddNodeTypeToSearchIndex(NodeSearchElement node, Document doc)
SetDocumentFieldValue(doc, nameof(LuceneConfig.NodeFieldsEnum.Parameters), node.Parameters ?? string.Empty);

writer?.AddDocument(doc);
InitializeIndexSearcher();
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure if this specific call is needed due that usually after a node info is indexed (or all the nodes info were indexed ) we call CommitWriterChanges() - and this call is already calling the InitializeIndexSearcher() method.

dirReader = writer != null ? writer.GetReader(applyAllDeletes: true) : DirectoryReader.Open(indexDir);
Searcher = new(dirReader);
}

Copy link
Contributor

Choose a reason for hiding this comment

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

yes, I think all of them were covered.

@RobertGlobant20
Copy link
Contributor

Coudl you add a GIF showing the behavior of InCanvas Search and Library Search?

{
if (DynamoModel.FeatureFlags?.CheckFeatureFlag("searchbar_debounce", false) ?? false)
{
searchDebouncer ??= new ActionDebouncer(dynamoViewModel?.Model?.Logger);
}

if (DynamoModel.FeatureFlags?.CheckFeatureFlag("searchbar_spearate_thread", false) ?? false)
Copy link
Member

Choose a reason for hiding this comment

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

does this exist?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have to create it. THanks !

Copy link
Member

Choose a reason for hiding this comment

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

spelling separate

@@ -78,6 +80,10 @@ public bool BrowserVisibility
internal int searchDelayTimeout = 150;
// Feature flags activated debouncer for the search UI.
internal ActionDebouncer searchDebouncer = null;
// Cancel token source used for the node search operations.
internal CancellationTokenSource searchCancelTooken;
Copy link
Member

Choose a reason for hiding this comment

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

spelling token

}, searchCancelTooken.Token).ContinueWith((t, o) =>
{
// This continuation will execute on the UI thread (forced by using FromCurrentSynchronizationContext())
searchResults = new List<NodeSearchElementViewModel>(t.Result);
Copy link
Member

@mjkkirschner mjkkirschner Jan 22, 2025

Choose a reason for hiding this comment

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

so, there should never be concurrent access to this collection because we always cancel when a search is performed?

oh, and it's always on the UI thread as you commented... duh.

{
dirReader = writer != null ? writer.GetReader(applyAllDeletes: true) : DirectoryReader.Open(indexDir);
Searcher = new(dirReader);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

not sure if I should lock this, expecting concurrent access to Searcher
All the indexing changes will probably not happen while a user is actively searching ...

@@ -972,11 +1004,11 @@ private void IsSelectedChanged(object sender, PropertyChangedEventArgs e)
/// </summary>
/// <returns> Returns a list with a maximum MaxNumSearchResults elements.</returns>
/// <param name="search"> The search query </param>
internal IEnumerable<NodeSearchElementViewModel> Search(string search)
internal IEnumerable<NodeSearchElementViewModel> Search(string search, CancellationToken ctk = default)
Copy link
Member

Choose a reason for hiding this comment

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

can you update the params?

{

ctk.ThrowIfCancellationRequested();
Copy link
Member

Choose a reason for hiding this comment

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

where do all these get caught?

@mjkkirschner
Copy link
Member

this seems like a pretty good change, not too hard to reason about.
Any idea if concurrent tests are going to cause issues, I remember some kind of past problem with locked index files.

Do you know if Lucene is itself spinning up new threads or using the thread pool, or touts itself as thread safe, or at least not requiring the main thread?

Also, it just makes me think there's probably something interesting we can do with anticipating searches and performing them in parallel using autocomplete info in the future.

{
var result = Entries.Where(e => {
if (e.Name.Replace(" ", string.Empty).Equals(nodeName) && e.FullCategoryName.Equals(nodeCategory))
{
ctk.ThrowIfCancellationRequested();
Copy link
Contributor

Choose a reason for hiding this comment

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

Where is this caught? Why can't the throw be at the start of the FindModel function?


if (DynamoModel.FeatureFlags?.CheckFeatureFlag("searchbar_spearate_thread", false) ?? false)
{
enableSeachThreading = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

enableSearchThreading

@@ -87,7 +87,9 @@ internal FeatureFlagsClient(string userkey, string mobileKey = null, bool testMo
//in tests we want instancing on so we can test it.
{ "graphics-primitive-instancing", LdValue.Of(true) },
//in tests we want search debouncing on so we can test it.
{ "searchbar_debounce", LdValue.Of(true) } });
{ "searchbar_debounce", LdValue.Of(true) },
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we still need this?

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.

4 participants