Skip to content

Conversation

@shubham1g5
Copy link
Contributor

@shubham1g5 shubham1g5 commented Feb 27, 2019

  • Adds a column for root level attributes in IndexedFixtureIndex user DB table
  • Adds AndroidIndexedFixtureInstanceTreeElement to support attributes lookup for root attibutes.

cross-request: dimagi/commcare-core#842

@shubham1g5 shubham1g5 changed the title Adds support for last_sync attribute for Indexed Fixtures Adds support for last_sync attribute for Indexed Fixtures Feb 27, 2019
@shubham1g5 shubham1g5 requested a review from ctsims February 27, 2019 06:45
@ctsims
Copy link
Member

ctsims commented Mar 1, 2019

Hi Shubham,

It seems a bit limiting for us to only be able to store a single attribute on the base element.

A lot of the dispatch downstream seems quite complex: https://github.com/dimagi/commcare-core/pull/842/files#diff-de4be0557361343fe019d6c9b25d8598R111

I'm wondering whether it would make more sense to instead serialize and store a single TreeElement which contains all of the core attributes. That element could be deserialized in whole, and then all of the attribute dispath methods in the element https://github.com/dimagi/commcare-core/pull/842/files#diff-de4be0557361343fe019d6c9b25d8598R95 could just be forwarded along, IE

    public String getAttributeName(int index) {
        return attributeHolderElement.getAttributeName(index)
    }

that would allow us to basically keep track of whatever other details need to be added in the future as well

@shubham1g5
Copy link
Contributor Author

@ctsims This is ready to be reviewed again.

TreeElement attrsHolder = null;
byte[] attrsBlob = c.getBlob(c.getColumnIndexOrThrow(INDEXED_FIXTURE_PATHS_COL_ATTRIBUTES));
if (attrsBlob != null) {
attrsHolder = SerializationUtil.deserialize(attrsBlob, TreeElement.class);
Copy link
Member

Choose a reason for hiding this comment

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

Additionally, I think we should have a concrete exception handling strategy for issues here, since deserialization exceptions are probably a very unexpected outcome.

It might be worth doing something like retrieving the byte[] here, and letting the TreeElement get deserialized at a later point (like in get() here: https://github.com/dimagi/commcare-core/pull/842/files#diff-de4be0557361343fe019d6c9b25d8598R38) when we are actively doing reads/deserializations from storage more broadly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Additionally, I think we should have a concrete exception handling strategy for issues here, since deserialization exceptions are probably a very unexpected outcome.

Do you mean that we don't want the app to crash on such deserialization exceptions and instead show some sort of error message to the user ?

Copy link
Member

Choose a reason for hiding this comment

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

Mostly I mean that having the app crash here in a deserialization exception would be extremely confusing because it would be a symptom of an event that's out of the user's control and would be handled in a very strange part of the lifecycle.

Generally speaking I think it's very, very dangerous for "Utility" classes like this which are static and effectively stateless to throw errors that aren't strongly meaningful at the time. To the programmer, this method looks like it's just receiving a db hook and a string and doing some lookups. The caller wouldn't be expecting to need to be ready to handle mismatched serialized internal models based on the signature and expectations.

That would be their expectation later, however, when they are actually managing the resulting lookup table, so pushing the deserialization code there might be cleaner.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ctsims Thanks. I get your point now. Though I am struggling to find a better place for the deserialization to go since it requires access to the Prototype factory and hence needs to be in the commcare-android. The get function you linked above is part of commcare-core. The only other place I can forsee is 1 level up i.e. getIndexedFixtureIdentifier but it again has the same semantics issue that doesn't convey to the caller to handle deserialization.

Alternatively, we can decide to not catch DeserializationException in SerializationUtil.deserialize which would add a throws DeserializationException into function signature and hence will be much more evident to a caller function.

Copy link
Member

Choose a reason for hiding this comment

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

Hm, I see what you are saying.

I can't think of a great workaround for now, and it is true that if we broke the TreeElement serialization, we'd need to be fixing the records from scratch anyway.

Maybe the big point of confusion here is just that it doesn't feel right for the attribute holder to be in this record to begin with. Can we make a second version of this method that doesn't pull the attrsHolder so that if we needed to just get the base and child paths, that there's a path for that? Would be good for this version to also throw a typed error specific to this problem so the end user (and us) can see very deliberately that it's an issue with the stored format for retrieving this.

I'm thinking about what pieces we'll need to have in place in a circumstance where we need to migrate fixtures due to a TreeElement serialization format change, and right now this method would just be a blocker.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ctsims

That makes a lot of sense though just to clarify are you suggesting that we create an unused function/ code path right now because we might need it in future ?

@shubham1g5
Copy link
Contributor Author

@damagatchi retest this please

3 similar comments
@shubham1g5
Copy link
Contributor Author

@damagatchi retest this please

@shubham1g5
Copy link
Contributor Author

@damagatchi retest this please

@shubham1g5
Copy link
Contributor Author

@damagatchi retest this please

@ctsims
Copy link
Member

ctsims commented Apr 17, 2019

@shubham1g5 I think we are both aligned on potential ways forward here, and sorry for all of the back and forth. i'm fine with whatever approach you want to take based on our conversations

@shubham1g5
Copy link
Contributor Author

@damagatchi retest this please

1 similar comment
@shubham1g5
Copy link
Contributor Author

@damagatchi retest this please

@shubham1g5
Copy link
Contributor Author

@ctsims This is ready to get reviewed again. Can you take another look.

}

@Override
protected AbstractTreeElement setupFixtureData(ExternalDataInstance instance) {
Copy link
Member

Choose a reason for hiding this comment

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

@shubham1g5 We should make sure that we've also implemented anything we need to implement here for Formplayer.

This may require, for example, require us to bump the version DB (which requires us to clear out Incomplete Forms).

Unrelated to this PR structure, but useful for us to know whether we need to update the Formplayer DB Version # with the next commcare-core pull in that library before merging this one (let me know if this comment doesn't make sense).

Copy link
Member

@ctsims ctsims left a comment

Choose a reason for hiding this comment

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

Hey, Shubham,

This looks great. Two small changes specifically in the Lazy Deserialization method, and one higher level question: Have we piloted to make sure FormPlayer works, and whether we need to apply a Version bump before it does (IE: It works out of the box, but if someone has already restore/loaded an indexed fixture, does it work to deserialize that first sandbox)

return getDeserializedAttributes().getAttributeValue(namespace, name);
}

private TreeElement getDeserializedAttributes() {
Copy link
Member

Choose a reason for hiding this comment

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

This method needs to be synchronized in the same way as this one:
https://github.com/dimagi/commcare-core/blob/master/src/main/java/org/commcare/cases/instance/StorageInstanceTreeElement.java#L103

to ensure that access to the attributes class is properly managed between threads.

We may want to consider a similar approach to that class where we have one "lazy load method" (there it's called loadElements()) that's called upfront before any methods, rather than specifically presuming that the lazy load is around the attributes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Made the method synchronized here though I am not sure of any particular benefits of doing deserialization ahead of time, specially since it requires us to be mindful of not accessing the attributes before we call the load on them. For ex. even though loadElements() is designed to be called once at the start we still end up calling it at couple other places.

Copy link
Member

Choose a reason for hiding this comment

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

Hi Shubham,

I think the lazy loading independent of attributes is just a stylistic thing, so fine for it to not be included.

-Clayton


private TreeElement getDeserializedAttributes() {
if (attributes == null) {
attributes = SerializationUtil.deserialize(attrHolder, TreeElement.class);
Copy link
Member

Choose a reason for hiding this comment

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

I think we may want to wrap serialization errors here, and specifically rethrow the error (still as a runtimeexception) with a message that says something like "Fixture Root Element Wrapper deserialization failed:" along with the real message, just so when this is seen and reported, we know exactly what is at error.

@shubham1g5
Copy link
Contributor Author

shubham1g5 commented Apr 22, 2019

@ctsims I have not checked it on Formplayer yet though assume it will require some changes.

  1. Do we want to add this root level attribute lookup support on formplayer as well ? (This will require a similar implementation asAndroidIndexedFixtureInstanceTreeElement on formplayer level)

  2. Or Is the concern here that the current core changes should not break existing functionality on Formplayer ? (This will require the DB upgrade for IndexedFixtureIndex table)

@ctsims
Copy link
Member

ctsims commented Apr 22, 2019

@shubham1g5

  1. I believe that we now should consider ourselves committed to making sure that core engine functionality such as this is implemented on both sides of the platform, since people primarily build and test their apps on Formplayer.
  2. Yes. My concern is specifically that we no longer have a clear process for determining when we can push commcare-core changes to FormPlayer, so if I, say, wanted to pull Fix for Short Cycle Detection Algorithm commcare-core#851 into formplayer's commcare-core branch, we aren't tracking data model changes that will require a UserDB version upgrade.

On the #2 point there - We don't need to migrate formplayer's db. I am fine continuing to kill the UserDB entirely by updating the version, but I don't think we should know when we are making changes that result in commcare-core being unpullable in FormPlayer in case we needed to manage an urgent issue.

@shubham1g5
Copy link
Contributor Author

@ctsims Nice points.

  1. I think that also mean that we should push related changes for both Mobile and formplayer in the same mobile release cycle which essentially means parallel development for both. Though to support such process in long term, we will need to establish that everyone who works on mobile also is responsible for corresponding feature development on formplayer.

  2. You are right that today we need to be careful to examine any DB changes in the core PR before merging it in formplayer. I am not sure if it's an overkill to give some label to these kind of PRs to make it more evident.

@shubham1g5
Copy link
Contributor Author

@ctsims bump to look at last set of changes here.

@ctsims
Copy link
Member

ctsims commented Apr 26, 2019

good from my side!

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.

3 participants