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

Adjust the DB check error message #1558

Merged
merged 8 commits into from
Nov 9, 2023
Merged

Adjust the DB check error message #1558

merged 8 commits into from
Nov 9, 2023

Conversation

infomiho
Copy link
Contributor

@infomiho infomiho commented Nov 3, 2023

Closes #1435

We had a regression with the way we handle DB server being available but the DB not existing.

  1. Before the DB check was introduced
    • we wouldn't check DB server connectivity
    • Prisma would create the DB if missing
  2. After the DB check was introduced
    • we check for DB server connectivity by trying to execute a dummy SQL command on the DB
    • Wasp fails with an error "DB server is not running"
  3. After the fix
    • we check for DB server connectivity by trying to execute a dummy SQL command on the DB
    • if the error is "DB doesn't exist" -> we tolerate it, since we know Prisma will create the DB if missing
    • in all other cases -> Wasp fails with an error "DB server is not running"

@infomiho infomiho requested review from Martinsos and sodic November 3, 2023 13:53
Copy link
Member

@Martinsos Martinsos left a comment

Choose a reason for hiding this comment

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

@infomiho I like the solution, and very nice job on writing the main description, it gave me all the context I needed!

I left comments mostly on naming / clean code and one very unlikely bug.

Comment on lines 153 to 157
textOutput <- collectJobTextOutputUntilExitReceived chan

-- "Database not created" error is fine since Prisma will create it for us.
let isErrorTolerated = any containsDatabaseNotCreatedError textOutput
return isErrorTolerated
Copy link
Member

Choose a reason for hiding this comment

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

I like how you wrote some "extra" code here to make it clearer what is happening!
I found textOutput confusing -> I thought it is a single text, while I believe it is lines, so I suggest making that clearer.
I also tried to make the code even a bit more explicit, as to the reasoning of what makes a check fail.

Suggested change
textOutput <- collectJobTextOutputUntilExitReceived chan
-- "Database not created" error is fine since Prisma will create it for us.
let isErrorTolerated = any containsDatabaseNotCreatedError textOutput
return isErrorTolerated
outputLines <- collectJobTextOutputUntilExitReceived chan
let databaseNotCreated = any containsDatabaseNotCreatedError outputLines
let checkSucceeded = databaseNotCreated
return checkSucceeded

return isErrorTolerated
where
-- Prisma error code for "Database not created" is P1003.
containsDatabaseNotCreatedError = T.isInfixOf "P1003"
Copy link
Member

Choose a reason for hiding this comment

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

So, while this will work in 99% cases, what if the prisma error text contains PIP1003 or P10030 or anything else that is not \bP1003\b? Then it will give false positive.

So to be 100% safe, I would do a bit fancier check here. Probably use regex \bP1003\b.

Comment on lines 51 to 58
collectJobMessages :: Chan J.JobMessage -> IO [J.JobMessageData]
collectJobMessages = collect []
where
collect messages chan = do
jobMsg <- readChan chan
case J._data jobMsg of
J.JobExit {} -> return messages
message -> collect (message : messages) chan
Copy link
Member

Choose a reason for hiding this comment

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

If you suffixed that other function with untilExitReceived, I would do the same here, since it behaves like that.

Copy link
Member

Choose a reason for hiding this comment

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

Btw I can't remember what is the difference between JobMessage and JobMessageData, but this function says it collects JobMessages but it returns JobMessageData, is that an issue? Should the name also have Data in it?

Copy link
Member

Choose a reason for hiding this comment

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

Aha ok, I realized it now from the casing in another function above. So JobMessageData is either Text or Exit? In that case, since you know you are returning only Text's here, why not return IO [Text]? It is more precise!

Comment on lines 51 to 58
collectJobMessages :: Chan J.JobMessage -> IO [J.JobMessageData]
collectJobMessages = collect []
where
collect messages chan = do
jobMsg <- readChan chan
case J._data jobMsg of
J.JobExit {} -> return messages
message -> collect (message : messages) chan
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
collectJobMessages :: Chan J.JobMessage -> IO [J.JobMessageData]
collectJobMessages = collect []
where
collect messages chan = do
jobMsg <- readChan chan
case J._data jobMsg of
J.JobExit {} -> return messages
message -> collect (message : messages) chan
collectJobMessages :: Chan J.JobMessage -> IO [J.JobMessageData]
collectJobMessages = collect []
where
collect jobMsgsData chan = do
jobMsg <- readChan chan
case J._data jobMsg of
J.JobExit {} -> return messages
jobMsgData -> collect (jobMsgData : jobMsgsData) chan

jobMessageToText :: J.JobMessageData -> Maybe Text
jobMessageToText = \case
J.JobOutput text _ -> Just text
J.JobExit _ -> Nothing
Copy link
Member

Choose a reason for hiding this comment

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

Although you know JobExit will never arrive here, right? But you wanted to satisfy the compiler? Ok, well it gives extra safety!

Copy link
Member

Choose a reason for hiding this comment

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

Ok now looking at the function below, collectJobMessages, it looks to me like you really should just merge the two functions into one, into collectJobTextOutputUntilExitReceived.

@infomiho infomiho requested a review from Martinsos November 8, 2023 10:14
Copy link
Member

@Martinsos Martinsos left a comment

Choose a reason for hiding this comment

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

Nice, looking good! I left a couple more comments but I think it is in good shape so I approved -> to with these comments as you wish and you can merge!

data DbConnectionTestResult
= DbConnectionSuccess
| DbNotCreated
| DbConnectionFailure
Copy link
Member

Choose a reason for hiding this comment

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

I would be tempted to add String to DbConnectionFailure that contains actual error message, is there a reason why you decided to drop that information?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because we don't get an error message per se, but we get some command output that might be useless unless parsed.

Comment on lines 171 to 172
containsDatabaseNotCreatedError :: T.Text -> Bool
containsDatabaseNotCreatedError text = text TR.=~ ("\\bP1003\\b" :: String)
Copy link
Member

Choose a reason for hiding this comment

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

I would probably put this in where under the testDbConnection. But maybe you took it out so you can write tests for it? If so, I understand. But then I would give it a bit more descriptive name: e.g. prismaOutputContainsDbNotCreatedError, because now it is missing the context it would have if it was in the where.

containsDatabaseNotCreatedError :: T.Text -> Bool
containsDatabaseNotCreatedError text = text TR.=~ ("\\bP1003\\b" :: String)

isDbConnectionEstablished :: DbConnectionTestResult -> Bool
Copy link
Member

Choose a reason for hiding this comment

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

I get what you do with this function, makes sense! I wonder a bit about name -> so we call that "connection established"? Even if database does not exist? It is not a bad name, but I wonder if it is precise enough. I guess the problem is I am not sure what "db connection established" really means.
Is there a better matching name? Maybe isDbInValidState? or isDbReady? isDbInUsableState? isDbReadyForPrisma? Something in that direction? If we can't find anything better, fine, let's leave it as it is, but I do feel that isDbConnectionEstablished could maybe be better.
If nothing, you could go with didDbConnectionTestSucceed.

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've went with isDbConnectionPossible since that's the check I was going for. The DB server can be connected to.

@@ -29,6 +31,15 @@ readJobMessagesAndPrintThemPrefixed chan = runPrefixedWriter go
J.JobOutput {} -> printJobMessagePrefixed jobMsg >> go
J.JobExit {} -> return ()

collectJobTextOutputUntilExitReceived :: Chan J.JobMessage -> IO [Text]
collectJobTextOutputUntilExitReceived = collect []
Copy link
Member

Choose a reason for hiding this comment

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

In haskell, usual idiom is to go with go, for functions like collect, as a name.

@infomiho infomiho merged commit 62aa2b7 into main Nov 9, 2023
4 checks passed
@infomiho infomiho deleted the miho-fix-db-init branch November 9, 2023 12:19
martinovicdev pushed a commit to martinovicdev/wasp that referenced this pull request Nov 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fix database initialization
2 participants