Skip to content
This repository has been archived by the owner on May 7, 2020. It is now read-only.

JSON storage BigDecimal scaling regression #3774

Closed
wborn opened this issue Jun 29, 2017 · 4 comments · Fixed by #3783
Closed

JSON storage BigDecimal scaling regression #3774

wborn opened this issue Jun 29, 2017 · 4 comments · Fixed by #3783

Comments

@wborn
Copy link
Contributor

wborn commented Jun 29, 2017

After updating to openHAB 2.1.0 I encounter again BigDecimal scaling issues. I.e. longs are stored as decimals in the jsondb files. Because of this the LIFX binding (and other bindings) revert to default values and log warnings like:

23:04:31.744 [WARN ] [inding.lifx.handler.LifxLightHandler] - Invalid value '300.0' for transition time, using default instead.
23:04:31.790 [WARN ] [inding.lifx.handler.LifxLightHandler] - Invalid value '300.0' for transition time, using default instead.
23:04:31.829 [WARN ] [inding.lifx.handler.LifxLightHandler] - Invalid value '0.0' for transition time, using default instead.
23:04:31.870 [WARN ] [inding.lifx.handler.LifxLightHandler] - Invalid value '300.0' for transition time, using default instead.
23:04:31.991 [WARN ] [inding.lifx.handler.LifxLightHandler] - Invalid value '300.0' for transition time, using default instead.

In org.eclipse.smarthome.core.thing.Thing.json the parameters show up like:

          "fadetime": 300.0

I'm using:

openhab> bundle:list|grep Json
136 | Active   |  80 | 0.9.0.b5               | Eclipse SmartHome Json Storage Service

@cdjackson originally fixed this with PR #2685.

@sjsf
Copy link
Contributor

sjsf commented Jun 30, 2017

I just checked the Json DB implementation - the mentioned workaround indeed got lost with #3225.

The database implementation itself actually handles precisions "correctly", i.e. it does not add the trailing .0 by itself. However, if it got in there (for whatever reasons), it keeps them.

The config normalization also ensures that INTEGER configuration values are converted to a a BigDecimal with 0 scale. So while I'd love to find out how these values originally get into the database, it still can't hurt to apply the same workaround again.

I will create a PR to make it more robust in this area.

sjsf pushed a commit to sjsf/smarthome that referenced this issue Jun 30, 2017
This basically re-introduces the same fallback mechanism
that was already implemented with eclipse-archived#2685 but got lost with

fixes eclipse-archived#3774
Signed-off-by: Simon Kaufmann <[email protected]>
sjsf pushed a commit to sjsf/smarthome that referenced this issue Jun 30, 2017
This basically re-introduces the same fallback mechanism
that was already implemented with eclipse-archived#2685 but got lost with

fixes eclipse-archived#3774
Signed-off-by: Simon Kaufmann <[email protected]>
sjsf pushed a commit to sjsf/smarthome that referenced this issue Jun 30, 2017
This basically re-introduces the same fallback mechanism
that was already implemented with eclipse-archived#2685 but got lost with eclipse-archived#3225.

fixes eclipse-archived#3774
Signed-off-by: Simon Kaufmann <[email protected]>
@sjsf
Copy link
Contributor

sjsf commented Jul 3, 2017

Oh, that's indeed a very ugly turn which I missed. Interesting... Very good analysis!

I looks like so far the put(...) actually did not serialize the object, but left it in the map as a plain java object. As a result, the "value" object gets serialized (and later deserialized) together with the "meta object" which is a Map. And by doing so, our humble StringObjectMapDeserializer delegates to Object type and therefore won't be considered for inner maps by Gson anymore. This is how some doubles/floats get in, which lead to the trailing .0. Therefore it makes a difference whether you request an object from the cache (i.e. what you had put in before) or you get it deserialized from the file. 😞

The proper fix of course would be to serialize the object to a strong before we put it into the outer map. However, that would lead to all objects being persisted as strings which include escaped json. Absolutely unreadable. That would contradict the original idea of the JsonDB: having a persistence format which is easily human-readable. Also it would render all existing JsonDBs useless.

Therefore I'll try to fix the StringObjectMapDeserializer in a more convenient way.

sjsf pushed a commit to sjsf/smarthome that referenced this issue Jul 3, 2017
As it turned out, the current Map deserializer was not very effective
in converting numbers to BigDecimals as it took itself out of the game
when deserializing the outer map which JsonStorage adds itself. Hence
a property map within an entity lost its normalization.

This could have been compensated again during read by simply cutting off
the trailing ".0"s, but this still led to the awkward situation that the
numbers in the serialized format were altered and looked different of
what would have been expected.

fixes eclipse-archived#3774
Signed-off-by: Simon Kaufmann <[email protected]>
@sjsf
Copy link
Contributor

sjsf commented Jul 3, 2017

I have updated the PR. As thanks to your help we now found the root-cause, I removed the workaround again for now.

I'd leave it open for discussion whether we should prefer rather stability (i.e. read the same what you wrote before) or automated error correction (i.e. cut off trailing .0 in case they got written into it).

@wborn @cdjackson @maggu2810 WDYT?

@wborn
Copy link
Contributor Author

wborn commented Jul 3, 2017

Thanks for having another look at the code @SJKA. After doing some more testing I again run into deserialization issues with the updated PR code. My "300" values again get deserialized into "300.0".

I've tested a new OH 2.2.0-SNAPSHOT, empty initial database and updated the Json Storage bundle so it contains the PR code.

I can actually reproduce this in the new unit tests by making the DummyObject a bit more complex so it looks more like the actual ThingImpls that are stored in the database. The following DummyObject makes testStableOutput() fail:

    private class DummyObject {

        public Map<String, Object> myMap = new HashMap<String, Object>();

        public List<Object> channels = new ArrayList<>();

        public DummyObject() {
            myMap.put("testShort", Short.valueOf("12"));
            myMap.put("testInt", Integer.valueOf("12"));
            myMap.put("testLong", Long.valueOf("12"));
            myMap.put("testDouble", Double.valueOf("12.12"));
            myMap.put("testFloat", Float.valueOf("12.12"));
            myMap.put("testBigDecimal", new BigDecimal(12));
            myMap.put("testBoolean", true);

            myMap.put("testString", "hello world");

            Map<String, Object> childMap = new HashMap<String, Object>();
            childMap.put("testChildLong", Long.valueOf("12"));
            channels.add(childMap);
        }
    }

The test fails because storageString2 then contains "testChildLong": 12.0.

sjsf pushed a commit to sjsf/smarthome that referenced this issue Jul 5, 2017
As it turned out, the current Map deserializer was not very effective
in converting numbers to BigDecimals as it took itself out of the game
when deserializing the outer map which JsonStorage adds itself. Hence
a property map within an entity lost its normalization.

This could have been compensated again during read by simply cutting off
the trailing ".0"s, but this still led to the awkward situation that the
numbers in the serialized format were altered and looked different of
what would have been expected.

Therefore this change
* Distinguishes the "outer map" (i.e. the internal data structure of the
  JsonStorage and the inner entity structures
* Uses two different Gson instances for handling those two structures
  individually
* Lets JsonStorage internally keep JsonElement structures _always_
* Registers a custom deserializer for Configuration which handles the
  custom number-to-BigDecimal conversion

fixes eclipse-archived#3774
Signed-off-by: Simon Kaufmann <[email protected]>
sjsf pushed a commit to sjsf/smarthome that referenced this issue Jul 5, 2017
As it turned out, the current Map deserializer was not very effective
in converting numbers to BigDecimals as it took itself out of the game
when deserializing the outer map which JsonStorage adds itself. Hence
a property map within an entity lost its normalization.

This could have been compensated again during read by simply cutting off
the trailing ".0"s, but this still led to the awkward situation that the
numbers in the serialized format were altered and looked different of
what would have been expected.

Therefore this change
* Distinguishes the "outer map" (i.e. the internal data structure of the
  JsonStorage and the inner entity structures
* Uses two different Gson instances for handling those two structures
  individually
* Lets JsonStorage internally keep JsonElement structures _always_
* Registers a custom deserializer for Configuration which handles the
  custom number-to-BigDecimal conversion

fixes eclipse-archived#3774
Signed-off-by: Simon Kaufmann <[email protected]>
sjsf pushed a commit to sjsf/smarthome that referenced this issue Jul 6, 2017
As it turned out, the current Map deserializer was not very effective
in converting numbers to BigDecimals as it took itself out of the game
when deserializing the outer map which JsonStorage adds itself. Hence
a property map within an entity lost its normalization.

This could have been compensated again during read by simply cutting off
the trailing ".0"s, but this still led to the awkward situation that the
numbers in the serialized format were altered and looked different of
what would have been expected.

Therefore this change
* Distinguishes the "outer map" (i.e. the internal data structure of the
  JsonStorage and the inner entity structures
* Uses two different Gson instances for handling those two structures
  individually
* Lets JsonStorage internally keep JsonElement structures _always_
* Registers a custom deserializer for Configuration which handles the
  custom number-to-BigDecimal conversion

fixes eclipse-archived#3774
Signed-off-by: Simon Kaufmann <[email protected]>
maggu2810 pushed a commit that referenced this issue Jul 19, 2017
* migrate json storage service test to java

Signed-off-by: Simon Kaufmann <[email protected]>

* fix potential NPE in json storage if backup folder does not exist

Signed-off-by: Simon Kaufmann <[email protected]>

* fix number deserialization on Json Storage

As it turned out, the current Map deserializer was not very effective
in converting numbers to BigDecimals as it took itself out of the game
when deserializing the outer map which JsonStorage adds itself. Hence
a property map within an entity lost its normalization.

This could have been compensated again during read by simply cutting off
the trailing ".0"s, but this still led to the awkward situation that the
numbers in the serialized format were altered and looked different of
what would have been expected.

Therefore this change
* Distinguishes the "outer map" (i.e. the internal data structure of the
  JsonStorage and the inner entity structures
* Uses two different Gson instances for handling those two structures
  individually
* Lets JsonStorage internally keep JsonElement structures _always_
* Registers a custom deserializer for Configuration which handles the
  custom number-to-BigDecimal conversion

fixes #3774
Signed-off-by: Simon Kaufmann <[email protected]>

* [jsondb] using dedicated storage entry object instead of a map

Signed-off-by: Simon Kaufmann <[email protected]>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants