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

Weird behavior (with "own" ProductUnit?) #353

Open
fawidmer opened this issue May 14, 2021 · 5 comments
Open

Weird behavior (with "own" ProductUnit?) #353

fawidmer opened this issue May 14, 2021 · 5 comments
Labels

Comments

@fawidmer
Copy link

Hello everyone

We are using indriya (v2.0.4) in a multi-thread environment. As I already pointed out a while back in #284, we have been facing some weird erratic behavior, which we were not able to reproduce. So we figured it might be some concurrency issue.

Unfortunately, we discovered recently, that the pull request above did not resolve our issues. Maybe, you have some ideas on how we could approach our issues or where they might come from.

Some info on what we're doing:

  • We have an own implementation of a "quantity", which consists basically of a java double and a javax.measure.Unit.

  • We have in a class a static field which we use for all of our dimensionless quantities: public static final Unit<Dimensionless> ONE = new ProductUnit<Dimensionless>(); See the attached file: Units.txt

  • Before starting our various threads, we are initializing some parameter objects in our main thread, (we are using reflections to call the respective constructors, don't know if that's of important). These parameters contain many different quantities.

  • After that, we start up various threads, some of which read (quantity) values from JSON files.

We have observed the following weird behavior:

  • On one day, reading variable X (from JSON) resulted in a value of 1.8749687871307178E9 with unit ONE, although the JSON raw content was 0.0 with unit ONE.
  • On another day (in a different JVM), one of our parameters Y, which is hard-coded as 0.95 with the unit ONE, was somehow converted again to a value of 1.8749687871307178E9 with unit ONE.
  • Within the same JVM, a few seconds later, the old variable X was again read from JSON. The raw value of 1.8749687871307178E9 with unit ONE was then strangely parsed as 0.98 with the unit ONE.
  • About a year ago (before 282 fixes ProductUnit#hashCode to be free of side-effects and thread-safe #284 was merged), we had similar issues with Units.OHM, where a value of 0.2 was suddenly converted to ~9.5557e+08 once, and ~9.8284e+08 another time...

Questions:

  • Do you have any idea where the mysterious value of 1.8749687871307178E9 might come from? Or in general, the numbers of approximate size 1E9?
  • Can we use the ProductUnit constructor like that? We are not adding the unit to any of the (hash)sets in any instance of AbstractSystemOfUnits. Is that a problem?
  • Do you see any other issues?

Thank you for any form of help, it is much appreciated!
Fabio

@keilw keilw added the analysis label May 14, 2021
@keilw
Copy link
Member

keilw commented May 14, 2021

@fawidmer Thanks for raising this, what keeps you from upgrading to Indriya 2.1.x?
I don't think being a very small team compared to something like Spring, Jakarta EE or the OpenJDK, we can afford to backport this if a fix was found, so it'll go into whatever 2.1.x version is then the current one (right now aiming at 2.1.3) but it would not be backported to anything like 2.0.x or even 1.x because they are legacy and unsupported just like JDK 9 or 14.

We have in a class a static field which we use for all of our dimensionless quantities: public static final Unit ONE = new ProductUnit(); See the attached file: Units.txt

Are you redeclaring or using AbstractUnit.ONE?
It is not so clear from the description, but above sounds like you created your own copy, and since AbstractUnit.ONE is often used internally by the RI and other modules on top of it, so cloning or "forking" that is not advisable, so is there a reason you did that?

Can we use the ProductUnit constructor like that? We are not adding the unit to any of the (hash)sets in any instance of AbstractSystemOfUnits. Is that a problem?

No, while the constructor currently is open and public you should not use it because it's entirely for use by the ONE constant.

ICU4J makes a distinciton between MeasureUnit and NoUnit in recent versions and I guess if we decided to go in a similar direction, we could (with a grace period and deprecating the existing constants) introduce a similar smaller SystemOfUnits implementations like NoUnits or NonUnits (also a bit like NonSI in si-units) which would be in the same package as ProductUnit, then the constructor (also maybe with a grace period of at least a minor version) would become package-private as it was more or less intended all the time.
The two constants in AbstractQuantity for consistency sake could then also be factored into the Quantities class.
As a first step, please if you created your own copy try to remove that and use AbstractUnit.ONE instead. And unless you have any strong reason against that try upgrade to Indriya 2.1.2. Even the Jackson library should at runtime work with a newer Indriya version, we have only very few cases where backward-compatibility was broken over time and this may not work, but we also plan to release new versions of the libraries during the Q2/21 Release Train.

@fawidmer
Copy link
Author

Thank you very much @keilw for the detailed feedback!

  • As you suggested, we will no longer use the constructor of ProductUnit, but just copy the reference of AbstractUnit.ONE. Do you think that might be related to our initial issue..?
  • As for upgrading to Indriya 2.1.x: I tried doing that, but ended up having issues with the Jackson library. I guess this is what you mentioned will be tackled for the next release. If not, then here's a minimal example on how to reproduce the error:
import javax.measure.Unit;
import javax.measure.quantity.Volume;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import tech.units.indriya.unit.Units;
import tech.uom.lib.jackson.UnitJacksonModule;

/**
 * @author fawidmer
 */
public class Temp {

    public static void main(String[] args) throws JsonProcessingException {

        ObjectMapper mpr = new ObjectMapper();
        mpr.registerModule(new UnitJacksonModule());

        Unit<Volume> value = Units.CUBIC_METRE;
        String content = mpr.writeValueAsString(value);
        Unit readValue = mpr.readValue(content, Unit.class);

    }

}

Error trace:

Exception in thread "main" java.lang.BootstrapMethodError: java.lang.NoClassDefFoundError: tech/units/indriya/ComparableUnit
	at systems.uom.ucum.format.UCUMFormat.<init>(UCUMFormat.java:206)
	at systems.uom.ucum.format.UCUMFormat$Parsing.<init>(UCUMFormat.java:486)
	at systems.uom.ucum.format.UCUMFormat$Parsing.<clinit>(UCUMFormat.java:481)
	at systems.uom.ucum.format.UCUMFormat.getInstance(UCUMFormat.java:111)
	at tech.uom.lib.jackson.UnitJacksonModule$UnitJsonSerializer.serialize(UnitJacksonModule.java:89)
	at tech.uom.lib.jackson.UnitJacksonModule$UnitJsonSerializer.serialize(UnitJacksonModule.java:69)
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319)
	at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:4094)
	at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3404)
	at ch.ethz.idsc.tensors.implementation.Temp.main(Temp.java:23)
Caused by: java.lang.NoClassDefFoundError: tech/units/indriya/ComparableUnit
	... 11 more
Caused by: java.lang.ClassNotFoundException: tech.units.indriya.ComparableUnit
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	... 11 more

@keilw
Copy link
Member

keilw commented Jun 9, 2021

@fawidmer Can this also be closed similar to unitsofmeasurement/uom-lib#68 or is there a separate issue left here?

@fawidmer
Copy link
Author

Dear @keilw
I'm not sure, since we still don't have any way of reproducing some of the behavior I mentioned above. It's possible that these measures fixed our issue:

  • Changing from our use of ProductUnit, to copying the reference of AbstractUnit.ONE.
  • Updating all dependencies.

I will come back to you if it persists, ok?

@keilw
Copy link
Member

keilw commented Jun 10, 2021

Ok thanks @fawidmer please verify and if it works now you can close the ticket you created yourself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants