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

Add writable flag to meta stores #256

Merged
merged 15 commits into from
Apr 1, 2022
Merged

Add writable flag to meta stores #256

merged 15 commits into from
Apr 1, 2022

Conversation

kiksekage
Copy link
Contributor

Fixes #255

The MetaStore class is set to be writable by default:
https://github.com/DHI-GRAS/terracotta/blob/a3aedcbfcd95aed279dd9754434b6409462b4bc1/terracotta/drivers/base_classes.py#L41

Added a very thin decorator to throw the new DatabaseNotWritable exception:
https://github.com/DHI-GRAS/terracotta/blob/a3aedcbfcd95aed279dd9754434b6409462b4bc1/terracotta/drivers/terracotta_driver.py#L23-L34

And then we access the flag directly when getting metadata from the meta store:
https://github.com/DHI-GRAS/terracotta/blob/22353ca9c3f99dbe4cdb70b7497d93162827713a/terracotta/drivers/terracotta_driver.py#L187-L194

Me and @nickeopti were discussing whether or not it was cleanest to handle the database "writability" in TerracottaDriver or all meta stores individually.

@codecov
Copy link

codecov bot commented Feb 28, 2022

Codecov Report

Merging #256 (f07de5e) into main (7011a23) will increase coverage by 0.22%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##             main     #256      +/-   ##
==========================================
+ Coverage   98.54%   98.76%   +0.22%     
==========================================
  Files          48       49       +1     
  Lines        2136     2194      +58     
  Branches      299      306       +7     
==========================================
+ Hits         2105     2167      +62     
+ Misses         19       17       -2     
+ Partials       12       10       -2     
Impacted Files Coverage Δ
terracotta/drivers/base_classes.py 100.00% <100.00%> (ø)
terracotta/drivers/relational_meta_store.py 98.92% <100.00%> (+0.01%) ⬆️
terracotta/drivers/sqlite_remote_meta_store.py 100.00% <100.00%> (ø)
terracotta/drivers/terracotta_driver.py 100.00% <100.00%> (ø)
terracotta/exceptions.py 100.00% <100.00%> (ø)
terracotta/server/flask_api.py 96.20% <100.00%> (+5.66%) ⬆️
terracotta/config.py 100.00% <0.00%> (ø)
terracotta/drivers/__init__.py 100.00% <0.00%> (ø)
terracotta/drivers/postgresql_meta_store.py 100.00% <0.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7011a23...f07de5e. Read the comment docs.

@dionhaefner
Copy link
Collaborator

dionhaefner commented Mar 1, 2022

I like introducing a new exception. But I think it would be cleaner if the meta stores themselves would check for writeability and raise the exception if necessary, and we can just let that bubble up to the client. Then we can also have this:

class RemoteSQLiteRasterStore:
    @requires_writable
    def insert(self, *args, **kwargs):
        pass  # always unwritable

In this case writable could be a private attribute self._writable, and all interactions could happen via this exception. For example, to raise a better error in conjunction with lazy loading we could catch the exception and re-raise it with a different message:

try:
    self.insert(keys, path, metadata=metadata) 
except DatabaseNotWritable as exc:
    raise DatabaseNotWritable("Lazy loading requires a writable database") from exc

@kiksekage
Copy link
Contributor Author

I moved the handling into the meta stores instead, with the explicit catching as well. If it looks good to you @dionhaefner I'll write some tests to up the coverage 👍

Copy link
Collaborator

@dionhaefner dionhaefner left a comment

Choose a reason for hiding this comment

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

Yes that looks pretty good. Thanks!

I think we will also need an error handler for the new exception but the tests should reveal that.

BTW, can we check whether the other drivers are writable via SQLalchemy? It should just boil down to checking whether the current user has write permissions on the used database.

terracotta/drivers/sqlite_remote_meta_store.py Outdated Show resolved Hide resolved
terracotta/drivers/terracotta_driver.py Show resolved Hide resolved
terracotta/exceptions.py Outdated Show resolved Hide resolved
@kiksekage
Copy link
Contributor Author

The failing macos tests go through now and the PR should be ready to merge if you have the time @dionhaefner @mrpgraae 👍

@dionhaefner
Copy link
Collaborator

I think we will also need an error handler for the new exception but the tests should reveal that.

BTW, can we check whether the other drivers are writable via SQLalchemy? It should just boil down to checking whether the current user has write permissions on the used database.

Could you follow up on these points? What's the status?

@kiksekage
Copy link
Contributor Author

Could you follow up on these points? What's the status?

Good point, I will have a look at that.

@kiksekage
Copy link
Contributor Author

BTW, can we check whether the other drivers are writable via SQLalchemy? It should just boil down to checking whether the current user has write permissions on the used database.

I can't seem to find any ORM/core way of doing this in sqlalchemy, though I might be looking in the wrong directions. This and this (very old) issue seem to indicate that interfacing with user privileges is done the raw SQL query way, and then we are back to having to discern between which backend we are using and what syntax to use.
I guess we could do a write to any/all of the four tables and a rollback at meta_store instantiation time, but that also doesn't sit right with me.

I think we will also need an error handler for the new exception but the tests should reveal that.

I confused myself, does this refer to adding exceptions.DatabaseNotWritableError to the API error handlers here: https://github.com/DHI-GRAS/terracotta/blob/b5ee523be89dba5a622633751f4181ad7f827246/terracotta/server/flask_api.py#L41

@dionhaefner
Copy link
Collaborator

Too bad with the permissions. Maybe doing it in SQL is not so bad? But then again it might not be portable between different DB vendors? Your call.

I confused myself, does this refer to adding exceptions.DatabaseNotWritableError to the API error handlers here:

Yes, exactly. Right now this will throw a 500 internal server error, but maybe we would want something more descriptive.

Copy link
Contributor

@nickeopti nickeopti left a comment

Choose a reason for hiding this comment

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

I think we will also need an error handler for the new exception but the tests should reveal that.

I confused myself, does this refer to adding exceptions.DatabaseNotWritableError to the API error handlers here:

Yeah, that will need a flask error handler, like the ones you referenced, @kiksekage. However, it seems that we don't have any tests for lazy-loading metadata on API/server level. I guess we should add that. I must admit I haven't quite grasped how that setup works, so I haven't added one.

terracotta/drivers/relational_meta_store.py Show resolved Hide resolved
terracotta/drivers/sqlite_remote_meta_store.py Outdated Show resolved Hide resolved
terracotta/drivers/terracotta_driver.py Show resolved Hide resolved
@kiksekage
Copy link
Contributor Author

Too bad with the permissions. Maybe doing it in SQL is not so bad? But then again it might not be portable between different DB vendors? Your call.

I think, with all the work that went into sqlachemy-fying the DB handling it hit me the wrong way to start doing some vendor-specific things again. I'm in favor of not checking it and letting it be up to the user.

I have added an error handler, and as far as I could tell, the only way for a API user to hit this error would be to request metadata for a dataset which is present but doesn't have any pre-calculated metadata. The test uses the already existing fixtures for generating sqlite-remote databases for testing so I moved that functionality to conftest.py.

@dionhaefner
Copy link
Collaborator

I think, with all the work that went into sqlachemy-fying the DB handling it hit me the wrong way to start doing some vendor-specific things again. I'm in favor of not checking it and letting it be up to the user.

Sounds like sunk cost fallacy to me :) But it's fine for now, the error from SQLalchemy should be descriptive enough to alert users as to what's going on.

tests/conftest.py Outdated Show resolved Hide resolved
Copy link
Collaborator

@dionhaefner dionhaefner left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks @kiksekage

@kiksekage
Copy link
Contributor Author

LGTM! Thanks @kiksekage

Should I do the honors of merging or will you @dionhaefner? 😄

@dionhaefner dionhaefner merged commit 0f975e7 into main Apr 1, 2022
@dionhaefner dionhaefner deleted the issue225-lazy-writeable branch April 1, 2022 10:22
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.

TerracottaDriver.get_metadata assumes meta store is writeable when lazy-loading metadata
3 participants