Skip to content

Backend compatible - #886

Merged
parsonsmatt merged 28 commits into
yesodweb:vlix/backend-compatiblefrom
Vlix:backend-compatible
Apr 15, 2019
Merged

parsonsmatt merged 28 commits into
yesodweb:vlix/backend-compatiblefrom
Vlix:backend-compatible

Conversation

@Vlix

@Vlix Vlix commented Apr 12, 2019

Copy link
Copy Markdown
Contributor

I've factored out what I've found to be redundant constraints in some SQL functions.
Most notably all instances of IsSqlBackend have been replaced or removed and BackendCompatible SqlBackend has been integrated where possible.

I've noticed a ton of redundant (at least seemingly) classes/types/etc.
After this refactor at least the following aren't noticeably used anywhere:

  • SqlReadBackend
  • SqlWriteBackend
  • IsPersistBackend
  • IsSqlBackend

If anyone can look this over, to see if I didn't interfere with the API, or some guarantee that's lost by fiddling with constraints, that'd be great.

Vlix and others added 27 commits March 1, 2018 14:21
* Upgraded the randomValues function in DataTestTypes.hs to actually have random values everytime
* small OCD
@parsonsmatt

parsonsmatt commented Apr 12, 2019 •

Copy link
Copy Markdown
Collaborator

SqlReadBackend and SqlWriteBackend are used by clients of the library. I know they're used extensively by Freckle, so as long as those types are still working good well after these changes then we should be good to go.

@Vlix

Vlix commented Apr 12, 2019

Copy link
Copy Markdown
Contributor Author

SqlReadBackend and SqlWriteBackend are used by clients of the library. I know they're used extensively by Freckle, so as long as those types are still working good well after these changes then we should be good to go.

If someone can check with whatever uses it, that'd be great, because I have no idea how they'd be used looking at this library alone.

@bitemyapp

bitemyapp commented Apr 12, 2019 •

Copy link
Copy Markdown
Contributor

@akurilin I suggest building this against your org repo. I can't do it for you. I remember y'all using those types too but I have no idea where things are at now.

@parsonsmatt

Copy link
Copy Markdown
Collaborator

I'm gonna write some tests for the feature.

@Vlix

Vlix commented Apr 12, 2019

Copy link
Copy Markdown
Contributor Author

Ok, well if the Read/Write types have a function, that still leaves the IsPersistBackend (and consequently IsSqlBackend) to not be used anywhere. If it is decided they are actually redundant, I can remove the definitions and instances of them.

Seeing as this change does shift almost all functionality to BackendCompatible, instead of HasPersistBackend, would that break people's programs?
It'd probably just give type errors when they update their persistent dependency, right?

@parsonsmatt

Copy link
Copy Markdown
Collaborator

The BackendCompatible stuff is used so that SqlReadBackend can be used for persistent entities that are defined for SqlBackend but only in read queries. Note that SqlReadBackend doesn't have any instances for the Write classes, so it can't do any updates to the DB. This is used to give certain parts of the program read-only access to the database. I wrote that class because the IsSqlBackend stuff was too restrictive for persistent-typed-db, where I had PersistEntityBackend record ~ SqlFor SomeDb.

I'd love it if the new changes were mostly "drop in." I think changing the functions to return a concrete Sqlbackend and then allowing clients to apply those functions manually will probably be a good thing - should make type inference a lot better and error messages easier to follow.

@parsonsmatt

Copy link
Copy Markdown
Collaborator

I think it may be a good idea to deprecate the IsSqlBackend-related stuff so we can give good deprecation warnings for an upgrade/migration path. ideally we only have one mechanism for saying "This backend is compatible with this other backend."

Comment thread persistent-sqlite/Database/Persist/Sqlite.hs Outdated
Comment thread persistent-sqlite/Database/Persist/Sqlite.hs Outdated
runSqlite :: (MonadUnliftIO m)
=> Text -- ^ connection string
-> ReaderT backend (NoLoggingT (ResourceT m)) a -- ^ database action
-> ReaderT SqlBackend (NoLoggingT (ResourceT m)) a -- ^ database action

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is another thing that should be easy to generalize on our side.

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.

Well, no, actually 😛

The entire point it that the backend/SqlBackend here, is from the open' function, which creates the SqlBackend in the withSqliteConn. Making a SqlBackend into a BackendCompatible SqlBackend backend => backend would imply creating a backend from a SqlBackend. That being annoying and undesirable was the entire point.
Remember, this is Sqlite specific, so it's gonna have to work with a SqlBackend.

The RawSqlite PR should work now afaict, though. Then the SqlBackend here would turn into a RawSqlite SqlBackend which is what merijn was trying to accomplish, I think.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

That's not quite right - BackendCompatible SqlBackend backend implies that you can create a SqlBackend from a backend. runSqlite would be able to run an action that uses SqlBackend, or SqlReadBackend, or SqlFor SomeDb, or RawSqlite SqlBackend (provided that was given an instance BackendCompatible SqlBackend (RawSqlite SqlBackend)).

The problem with IsSqlBackend is that it requires an isomorphism - that you can go from a SqlBackend to your type. RawSqlite SqlBackend has more than a mere SqlBackend, so there's no SqlBackend -> RawSqlite SqlBackend you can use.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We can try the polymorphic BackendCompatbile SqlBackend backend => backend stuff and if it presents problems with the raw SQLite stuff then we can revert those functions to concrete types.

It's not a deal breaker either way - requiring ReaderT SqlBackend ... just means that library users call local SqlReadBackend to map the types how they want instead of the library doing it automatically.

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.

I'll try it one more time, maybe I'm just derping with the type system.

-- Returns also the identifiers.
selectSource
:: (PersistQueryRead (BaseBackend backend), MonadResource m, PersistEntity record, PersistEntityBackend record ~ BaseBackend (BaseBackend backend), MonadReader backend m, HasPersistBackend backend)
:: (PersistQueryRead backend, MonadResource m, PersistEntity record, PersistRecordBackend record backend, MonadReader backend m)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This signature is so much nicer ❤️


createSqlPool
:: (MonadLogger m, MonadUnliftIO m, IsSqlBackend backend)
:: (MonadLogger m, MonadUnliftIO m, BackendCompatible SqlBackend backend)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hm, if these functions can return a BackendCompatible SqlBackend backend => backend then shouldn't the other {with,create}{Postgresql,MySQL,etc}Pool functions also be able to create that?

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.

Well, no, since those functions are actual implementations, they will create SqlBackends.

The only thing I can think of that wouldn't just imply using IsSqlBackend, would be an extra argument somewhere in which the user of the function can give the following function which will be called before creating the Pool:
BackendCompatible SqlBackend backend => SqlBackend -> backend
Maybe with a MonadIO m so they can use IO to create their own backend.

@akurilin

Copy link
Copy Markdown

@akurilin I suggest building this against your org repo. I can't do it for you. I remember y'all using those types too but I have no idea where things are at now.

@jdreaver is taking a look 👍

@parsonsmatt

Copy link
Copy Markdown
Collaborator

I added some tests in #887 though they're not super complete.

@Vlix

Vlix commented Apr 13, 2019

Copy link
Copy Markdown
Contributor Author

Ok, so here's the dealio:

This is the chain:

wrapConnectionInfo :: SqliteConnectionInfo -> Sqlite.Connection -> LogFunc -> IO SqlBackend
wrapConnectionInfo = {the entire SqlBackend is constructed here}

open' :: SqliteConnectionInfo -> LogFunc -> IO SqlBackend
open' connInfo logFunc = do
    conn <- Sqlite.open $ _sqlConnectionStr connInfo
    wrapConnectionInfo connInfo conn logFunc `E.onException` Sqlite.close conn

withSqliteConnInfo :: (MonadUnliftIO m, MonadLogger m)
                   => SqliteConnectionInfo -> (SqlBackend -> m a) -> m a
withSqliteConnInfo = withSqlConn . open'

withSqlConn
    :: (MonadUnliftIO m, MonadLogger m, BackendCompatible SqlBackend backend)
    => (LogFunc -> IO backend) -> (backend -> m a) -> m a
withSqlConn open f = do
    logFunc <- askLogFunc
    withRunInIO $ \run -> bracket
      (open logFunc)
      close'
      (run . f)

runSqlite :: (MonadUnliftIO m)
          => Text -- ^ connection string
          -> ReaderT SqlBackend (NoLoggingT (ResourceT m)) a -- ^ database action
          -> m a
runSqlite connstr action = runResourceT . runNoLoggingT $
    withSqliteConn connstr $ runSqlConn action

runSqlConn takes the action and will run it with whatever the open argument to withSqlConn is. In this case, that's the SqlBackend from open'. (withSqliteConn is just withSqliteConnInfo, but the Text is first turned into a SqliteConnectionInfo)
I honestly don't see a way to make that SqlBackend into anything else? I don't see a way to get around the fact that the database action ReaderT will receive a SqlBackend. What one does with it is up to them, I guess.

@parsonsmatt

Copy link
Copy Markdown
Collaborator

Okay, let's land this PR, then get the SQLite one working, and I'll give these open-etc functions a try to see if I can get them generalized without borking any expected functionality.

@parsonsmatt

parsonsmatt commented Apr 13, 2019 •

Copy link
Copy Markdown
Collaborator

I tried to test on my work codebase but I need a new version of persistent-typed-db for that to work out :) May get that done next week if @jdreaver hasn't reported back.

EDIT: This builds fine on the work codebase, however, we don't use SqlReadT or SqlWriteT anywhere, so not a great point of comparison. I'll try it on esqueleto too, which does use it.

Esqueleto works with this PR.

@parsonsmatt parsonsmatt added this to the 2.10 milestone Apr 13, 2019
@parsonsmatt

Copy link
Copy Markdown
Collaborator

@Vlix There are some merge conflicts from your other PR. Let's get those resolved and merge this in. Freckle and folks can test on master and if it's not working then we can figure out how to work around it there.

@merijn

merijn commented Apr 15, 2019

Copy link
Copy Markdown
Contributor

I think it may be a good idea to deprecate the IsSqlBackend-related stuff so we can give good deprecation warnings for an upgrade/migration path. ideally we only have one mechanism for saying "This backend is compatible with this other backend."

Agreed and I'm very happy the (more flexible) BackendCompatible ends up the final winner with this PR :)

@Vlix once this PR is merged I'll try and make some time to revisit the SQLite bits you mentioned, since those needed changing in my other PR and since it's grossly out of date by now I need to revisit it anyway.

@parsonsmatt
parsonsmatt changed the base branch from master to vlix/backend-compatible April 15, 2019 21:17
@parsonsmatt
parsonsmatt merged commit a52af69 into yesodweb:vlix/backend-compatible Apr 15, 2019
@parsonsmatt

Copy link
Copy Markdown
Collaborator

I'm going to get this PR finished up

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.

6 participants