Skip to content

Conversation

@NickCraver
Copy link
Collaborator

@NickCraver NickCraver commented Mar 16, 2022

To help in reviewing this, I've provided an overview to listen along while reviewing.

This is exploring what NRTs (nullable reference types) would look like on the main library. This covers the migration + test migration (to get a feel for what users would see). Highlights are:

  • We return empty arrays where a null could have happened before to keep it non-nullable (e.g. for FireAndForget on a fetch).
  • RedisKey/RedisValue/friends are string? which changes a lot of (string) casts.
  • Script executions are always non-null (I think this was effectively the case before).
  • IConvertible is object and not object? and so RedisValue and RedisResult are...just lying. They can be null. I have no idea why the decision on not-nullable is the way it is.
    • Same for IConvertible.ToType, lying again.
  • [return: NotNullIfNotNull("field")] doesn't work like you'd think on Task<T>/Task<T?>, so we have 2 versions of ExecuteAsync trees for nullable vs. non-nullable (e.g. non-nullable default value provided, like with the arrays mentioned above).
    • Note that generic overrides need where T : default here. Don't ask for details. Because I don't really understand why.
  • .IsNullOrEmpty() and .IsNullOrWhiteSpace() extensions are just easier due to lack of annotations on full framework.
  • This pattern is common, and we should maybe change it (very repetitive):
    • Perhaps an aggressive inline of a TryGetException? Don't want to make the 99.9% success case more expensive though.
if (result != WriteResult.Success)
{
    var ex = Multiplexer.GetException(result, message, this)!;
  • Also common is GetBridge(message)!, which isn't null if create is true, so maybe break this into 2 methods with no create arg, one of which isn't nullable? (TryGetBridge isn't great because this is most often chained).
    • This returns null from the disposed path everywhere...should probably revisit.

}
return i % 2 == 0 ? null : Task.CompletedTask;
// TODO: What the freaking hell was this ever trying to do?
return i % 2 == 0 ? null! : Task.CompletedTask;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I...wut?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

cc @mgravell ^

Copy link
Contributor

@TimLovellSmith TimLovellSmith Jul 2, 2022

Choose a reason for hiding this comment

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

It's a test so... two scenarios for the price of one?? Maybe?

@NickCraver NickCraver marked this pull request as ready for review March 22, 2022 15:09
@NickCraver NickCraver changed the title WIP: Nullable Reference Types for main library Nullable Reference Types for main library Mar 26, 2022
@NickCraver
Copy link
Collaborator Author

@slorello89 I'd love thoughts here anywhere, but specifically on return types (e.g. from IDatabase and IServer and async interfaces) as to what's nullable and if any strike you as incorrect. I know that's a big ask, but if you had the time to scroll those and give thoughts on any tweaks, I'd appreciate it!

This removed the parent bits and just accepts a null case, code before this was likely an iteration that just didn't get all the way to simplest.
In prep for more PRs and easier reviews, turning a lot of the language features on that I've addressed as part of NRTs but doing further here while having to review all changes anyhow. These are minor, but align on styles.
Copy link
Collaborator

@slorello89 slorello89 left a comment

Choose a reason for hiding this comment

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

Hey @NickCraver,

I went through the rest of what was in here, had a couple of questions about things that were null and a few other minor comments.

There is an awful lot of what looks like code cleanup changes in the Wrapper classes.

I scrolled through the entirety of IDatabase and IServer, for potentially missing threads to pull on, I only found a few inconsistencies in there (my questions in IServer are fleshed out in the other comments.

But a few things in IDatabase:

  • KeyRandom can return a null key if Redis is empty
  • It looks Like the HashEntry[] result for HashGetAll MIGHT be null? - the Parse method in the ResultProcessor - looks like it can pull back a null array.
  • Is the result of IdentifyEndpoint potentially null?

Overall looks really good, obviously, there are questions as to whether or not this would be a minor or major version revision for the library. Putting that aside, I don't think I saw anything that I would insist on changing.

Also: think it's missing release notes :)

There are things we're removing completing in 3.0 which means actually binary breaks potentially, vs. people opting into warnings as errors on a 2.x release - decision is to go with a minor here and embrace warnings as warnings. If you want to hard break on warnings (like our existing `[Obsolete(error = false)]` additions), so be it.
Return an empty array instead (key is already the null indicator - an empty array is good here).
If we can't parse a response, properly throw here and go not nullable.
@NickCraver NickCraver requested a review from slorello89 April 4, 2022 12:45
@Avital-Fine Avital-Fine mentioned this pull request Apr 4, 2022
Copy link
Collaborator

@slorello89 slorello89 left a comment

Choose a reason for hiding this comment

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

1 weird comment that looks like it was probably an accidental copy/paste out of a different PR.

Otherwise, I think this looks pretty set.

I think calling this a Minor vs Major release is the right move, emitting warnings is fair in a minor IMO.

return db;
}

/// <summary>
Copy link
Collaborator

Choose a reason for hiding this comment

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

this comment seem like it was accidentally pasted?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

wooops, what the hell Nick?

Copy link
Collaborator

@slorello89 slorello89 left a comment

Choose a reason for hiding this comment

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

LGTM 👍

Copy link
Collaborator

@mgravell mgravell left a comment

Choose a reason for hiding this comment

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

I've looked through this, focusing on the stuff under Interfaces (the primary public API), and it looks good to me. A few nice tweaks around Try etc. I'm not personally too concerned if we need to tweak some of the NRT annotations later, but: looks good to me. Nice job, sir.

/// </summary>
public override string ToString()
{
if (toString != null) return toString;
Copy link
Collaborator

Choose a reason for hiding this comment

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

❤️ you care!

@NickCraver NickCraver merged commit 75da236 into main Apr 8, 2022
@NickCraver NickCraver deleted the craver/nullable branch April 8, 2022 00:22
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.

6 participants