Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

refactor: handles Cursor uniformly over all usage provenances#64319

Merged
kritzcreek merged 1 commit into
mainfrom
christoph/unify-cursor-handling
Aug 7, 2024
Merged

refactor: handles Cursor uniformly over all usage provenances#64319
kritzcreek merged 1 commit into
mainfrom
christoph/unify-cursor-handling

Conversation

@kritzcreek

@kritzcreek kritzcreek commented Aug 7, 2024

Copy link
Copy Markdown
Contributor

This change should not change any behaviour, but it prepares for syntactic/search-based pagination.

Both syntactic and search based usages now return a "NextCursor", which is Some if there are more results of their provenance to be had. For now they always return None which preserves the current behaviour. The logic in root_resolver implements a state machine that transitions the cursor through the provenances and makes sure to skip provenances if the cursor in the request starts a certain state.

Reviewer hint: I wouldn't bother with the diff view for root_resolver.go

Test plan

Tests continue to pass, no behavioral changes

This change should not change any behaviour for now, but it prepares
for syntactic/search-based pagination
@cla-bot cla-bot Bot added the cla-signed label Aug 7, 2024
@github-actions github-actions Bot added team/graph Graph Team (previously Code Intel/Language Tools/Language Platform) team/product-platform labels Aug 7, 2024

@varungandhi-src varungandhi-src left a comment

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.

I find the state transitions as done a bit confusing, but leaving the final decision up to you

Comment on lines +276 to +277
if cursor.IsPrecise() && !provsForSCIPData.Precise {
cursor = codenav.UsagesCursor{CursorType: codenav.CursorTypeSyntactic}

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.

Shouldn't it be an error to end up in this state? More generally, depending on the original request, the cursor should be undergoing one of the following state transitions sequences:

  • None -> Precise -> Syntactic -> Search-based -> Done
  • None -> Precise -> Done
  • None -> Syntactic -> Done
  • None -> Search-based -> Done

So I think it should not be possible to have the cursor to be in this state unless someone deliberately sent a malformed request...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

With the way things are currently set up that's true, but I don't see why we shouldn't at some point be able to request "Syntactic + Search-based" or "Precise + Search-based"(to emulate IntelliJ's rename that also targets comments)

Comment on lines +284 to +289
cursor = nextPreciseCursor.UnwrapOr(codenav.UsagesCursor{CursorType: codenav.CursorTypeSyntactic})
}

gitTreeTranslator := r.MakeGitTreeTranslator(&args.Repo)

var previousSyntacticSearch core.Option[codenav.PreviousSyntacticSearch]
if remainingCount > 0 && provsForSCIPData.Syntactic {
syntacticResult, prevSearch, err := r.svc.SyntacticUsages(ctx, gitTreeTranslator, usagesForSymbolArgs)
if err != nil {
switch err.Code {
case codenav.SU_Fatal:
return nil, err
case codenav.SU_NoSymbolAtRequestedRange:
case codenav.SU_NoSyntacticIndex:
case codenav.SU_FailedToSearch:
default:
// None of these errors should cause the whole request to fail
// TODO: We might want to log some of them in the future
}
} else {
for _, result := range syntacticResult.Matches {
usageResolvers = append(usageResolvers, NewSyntacticUsageResolver(result, args.Repo.Name, args.CommitID))
}
numSyntacticResults = len(syntacticResult.Matches)
remainingCount = remainingCount - numSyntacticResults
previousSyntacticSearch = core.Some(prevSearch)
if cursor.IsSyntactic() && !provsForSCIPData.Syntactic {
cursor = codenav.UsagesCursor{CursorType: codenav.CursorTypeSearchBased}
}

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.

I think it would be better to advance the cursor directly from Precise -> Done, or from Syntactic -> Done instead of setting it and then immediately skipping a branch. It seems weird for the cursor to get into a syntactic state when provsForSCIPData.Syntactic is false.

I understand that this branch here is basically handling the cursor being set to CursorTypeSyntactic inside the UnwrapOr, but I think it would be better to introduce a separate function and use that, something like:

func (ty CursorType) AdvanceCursor(nextCursor core.Option[UsagesCursor], provenances ForEachProvenance[bool]) (newCursor UsagesCursor) {
    if next, ok := nextCursor.Get() {
        return next
    }
    if ty.IsPrecise() && provenances.Syntactic {
        return UsagesCursor{CursorType: codenav.CursorTypeSyntactic}
    } else if (ty.IsPrecise() || ty.IsSyntactic()) && provenances.SearchBased {
        return UsagesCursor{CursorType: codenav.CursorTypeSearchBased}
    } else {
        return UsagesCursor{CursorType: codenav.CursorTypeDone}
    }
}

Or you could have func (ty CursorType) NextCursor(provenances ForEachProvenance[bool]) UsagesCursor and call that inside UnwrapOr.

@kritzcreek

Copy link
Copy Markdown
Contributor Author

I tried different variants of skipping directly to the next cursor state but always ended up duplicating logic somewhere.

I'm pretty happy with the current solution because it basically lets you drop any Cursor anywhere in the control-flow and things will come out right. I'll merge like this for now, but am happy to revisit this, if it ends up causing problems the next time we need to change this logic.

@kritzcreek kritzcreek merged commit f1d5d77 into main Aug 7, 2024
@kritzcreek kritzcreek deleted the christoph/unify-cursor-handling branch August 7, 2024 06:26
@varungandhi-src

Copy link
Copy Markdown
Contributor

I tried different variants of skipping directly to the next cursor state but always ended up duplicating logic somewhere.

I don't see how the point about code duplication applies here? My suggestion was about consolidating the state transition logic into a single place. Why would that incur extra duplication?

because it basically lets you drop any Cursor anywhere in the control-flow and things will come out right

I don't think this is quite true (if the syntactic logic somehow incorrectly returned a precise cursor, then we'd skip search-based because the check is for cursor.IsSyntactic()), nor do I think it would be a good thing even if it were true (because it risks hiding bugs with the cursor having entered an illegal state).

I'd rather have the code fail loudly (in this case, we don't want to panic, but we can Error and exit early) than keep chugging along silently for a case which we haven't anticipated.

I don't think the state transitions here are that complicated, so it's not a big deal, but I'm surprised by you pushing back on an alternate suggestion which avoids getting the cursor into an illegal state.

I get your point about us changing the traversal in the future, but if that's the case, wouldn't it be better to have the state transitions in one place rather than in multiple places?

@kritzcreek

Copy link
Copy Markdown
Contributor Author

I don't see how the point about code duplication applies here? My suggestion was about consolidating the state transition logic into a single place. Why would that incur extra duplication?

I couldn't make it work with a single transition function that also allows accepting the initial cursor. But I've come around to having an InitialCursor : Provs -> UsageCursor function not really being a bad thing.

I don't think this is quite true (if the syntactic logic somehow incorrectly returned a precise cursor, then we'd skip search-based because the check is for cursor.IsSyntactic()), nor do I think it would be a good thing even if it were true (because it risks hiding bugs with the cursor having entered an illegal state).

True, I was thinking about the initial cursor here, the "anywhere in the control flow" was nonsense. Still that's where I ended up having to debug while implementing this. I took your suggestion and ran with it here: https://github.com/sourcegraph/sourcegraph/pull/64321

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

Labels

cla-signed team/graph Graph Team (previously Code Intel/Language Tools/Language Platform) team/product-platform

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants