Skip to content

Upgrade the dependency requirement of SqlAlchemy#2113

Merged
sphuber merged 3 commits into
developfrom
fix_465_upgrade_sqlalchemy
Nov 9, 2018
Merged

Upgrade the dependency requirement of SqlAlchemy#2113
sphuber merged 3 commits into
developfrom
fix_465_upgrade_sqlalchemy

Conversation

@lekah
Copy link
Copy Markdown

@lekah lekah commented Oct 24, 2018

Fixes #465

This fixes the long-standing issue of upgrading SQLAlchemy.
The way attributes stored in a JSON were queried had to be chained.
Additional tests were added to see this kind of errors before.
In essence, the issue was that the type check and value check were in arbitrary order.
Now a case statements enforces type check before value check.

@lekah lekah changed the title Fix 465 upgrade sqlalchemy - WIP Fix 465 upgrade sqlalchemy Oct 25, 2018
@giovannipizzi
Copy link
Copy Markdown
Member

Fixes #465

for tag, projected_entities_dict in tag_to_projected_entity_dict.items()
}
else:
raise Exception("Got an empty dictionary")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Apart for the test that is failing (that I guess it's simple to fix), could you replace generic raised Exceptions with appropriate ones (here and elsewhere)? Either defined in aiida.common.exceptions, or most probably it's enough to use the standard Python ones (e.g. here I imagine a ValueError, other times it might be a TypeError or similar).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

OK for the exceptions, I put in more appropriate (b037028)
I can't reproduce the failing test locally though.

@giovannipizzi
Copy link
Copy Markdown
Member

For the pre-commit tests that fail, I added a documentation here on how to install locally pre-commit and fix the problems:
https://github.com/aiidateam/aiida_core/wiki/Contributors-technical-guide#installing-the-precommit-hooks

@coveralls
Copy link
Copy Markdown

coveralls commented Oct 26, 2018

Coverage Status

Coverage increased (+7.7%) to 68.583% when pulling 095c0d3 on fix_465_upgrade_sqlalchemy into 657ad68 on develop.

@sphuber sphuber changed the title Fix 465 upgrade sqlalchemy Upgrade the dependency requirement of SqlAlchemy Nov 7, 2018
@sphuber sphuber force-pushed the fix_465_upgrade_sqlalchemy branch from 23c5750 to 979d42a Compare November 7, 2018 17:14
@sphuber
Copy link
Copy Markdown
Contributor

sphuber commented Nov 7, 2018

I fixed the problem that was failing the tests from running on Travis for SqlAlchemy. It was preventing from the pre scripts to even finish. Now the tests are running and there are still some failing tests. I also took the liberty to squash and clean up your commits.

@sphuber sphuber force-pushed the fix_465_upgrade_sqlalchemy branch from 979d42a to 71e2132 Compare November 7, 2018 19:05
@sphuber
Copy link
Copy Markdown
Contributor

sphuber commented Nov 7, 2018

The only remaining issue has to do with the flag_modifed function and the ModelWrapper. Even though my solution solved the issues in quite a few cases, in this test it didn't or maybe caused it. Essentially, we have a computer instance, set the metadata on it through _set_metadata, which calls flag_modified which excepts because the model instance content does not have the _metadata key. This seems related to a model instance that is not properly updated, because if I print (i.e. refresh the db node instance) before the flag modified, then everything works. Therefore I am pinging @muhrin he has dealt with these Heisenbugs before I think

@giovannipizzi
Copy link
Copy Markdown
Member

I've at least isolated the problem: in aiida.backends.sqlalchemy.models.base, function Model.save, the DbComputer gets "corrupted" when committing the session (sess.commit()).
In fact, right before self.__dict__ contains e.g.

{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x1137ac0d0>, 'uuid': UUID('f7364281-1841-4bc0-b717-8103212e2ef3'), 'transport_params': {}, 'hostname': u'localhost', 'enabled': True, 'name': u'localhost_1', '_metadata': {u'workdir': u'/tmp/aiida'}, 'transport_type': u'local', 'scheduler_type': u'pbspro', 'id': 2285, 'description': u''}

while right after it is

{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x1137ac0d0>} 

As you see _metadata is gone together with all other fields, and therefore the error.

I don't know why yet, and neither if this is caused by the wrapping/unwrapping done by the tests (the computer is generated by the test infrastructure) + the ModelWrapper, or it's another hidden problem.

Any suggestions?

Leonid Kahle and others added 2 commits November 9, 2018 09:20
To make the code base compatible with the new version, expressions produced
by the querybuilder had to include casts from JSONB. The cast that needs to
happen depends on the type of the value, but the order of the type check
and the cast cannot be guaranteed in the old syntax. Therefore we
replace the attribute queries from chains to cases, such that the type
is checked before it is casted.
The new backend wraps the database model instances in the `ModelWrapper`
utility class, which will take care of timely flushing and updating the
model instance upon changes. Across the code, model instances are passed
to the `flag_modified` functionn of SqlAlchemy that mark a certain key
on an instance as modified. However, sometimes instead of the actual
model instance being passed, the wrapped version is passed. This used to
be ignored for `sqlalchemy==1.0.19` but in `sqlalchemy==1.2.12` to which
we have updated, an `InvalidRequestError` is raised.

To overcome this, we define our own `flag_modified` function that wraps
the one of SqlAlchemy and will unwrap any wrapped instances that are
passed to it.
@sphuber
Copy link
Copy Markdown
Contributor

sphuber commented Nov 9, 2018

I found the problem. The flag_modified function of SqlAlchemy is supposed to remove the attributes from the dict_ after it has updated the model instance. This is why the values seemed to disappearing. The problem is that flag_modified was then called again, at which point the _metadata key no longer exists, because it had already been flagged and the code crashes. This never used to crash, because in SqlA this check was not there and just did nothing silently. In 1.2.12 it raises an exception. The reason flag_modified is called twice is because it is done in the __setattr__ of the ModelWrapper, through the _flush method, as it should be, but then flag_modified is called a second time in SqlaComputer._set_metadata, which is the one that excepts

@sphuber sphuber closed this Nov 9, 2018
@sphuber sphuber reopened this Nov 9, 2018
The `_set_metadata` method of `SqlaComputer` was calling `flag_modified`
manually after having set the attribute, however, since the model is
wrapped in the `ModelWrapper`, it's `__setattr__` will take care of
calling `flag_modified` when it flushes upon an attribute being updated
for a stored entity. Calling `flag_modified` on the same key twice will
except in SqlAlchemy 1.2.12 because the "dirty" attribute has already
been removed and moved into the database model instance.
@sphuber sphuber force-pushed the fix_465_upgrade_sqlalchemy branch from 71e2132 to 095c0d3 Compare November 9, 2018 08:39
Copy link
Copy Markdown
Member

@giovannipizzi giovannipizzi left a comment

Choose a reason for hiding this comment

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

Looks great! If tests pass, it's good to merge!! :-)

@sphuber sphuber merged commit 4fc84c0 into develop Nov 9, 2018
@sphuber sphuber deleted the fix_465_upgrade_sqlalchemy branch November 9, 2018 09:23
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.

Upgrade SQLA to version >1.1 (and check why there are casting problems)

4 participants