SAP / cloud-sdk

The SAP Cloud SDK documentation and support repository.
https://sap.github.io/cloud-sdk/
Apache License 2.0
44 stars 41 forks source link

Cloud SDK Possible Concurrency Issue with Json Serialization Version = 3.56.0 #523

Closed newcron closed 2 years ago

newcron commented 2 years ago

Issue Description

I am synchronizing prices between an external system and SAP HANA Cloud using the client.

As there is a big number of prices to be synched, these syncs are running asynchronousely in a thread pool. I found that some of the requests fail with the below exception leading to data inconsistency issues. Seems to me that there might be an issue with thread safetiness

The exceptions are shown below.

Code looks like this:

 @SneakyThrows
    @Async
    public Future<Void> saveAsync(ComparableRuleData x, TaskMonitor mon) {

        new DefaultSalesPricingConditionRecordService().createSlsPrcgConditionRecord(SlsPrcgConditionRecord.builder()
                .conditionTable("305")
                .conditionType("PPR0")
                .conditionRateValue(x.getMoney().setScale(2, RoundingMode.HALF_UP))
                .conditionRateValueUnit(x.getCurrency().name())

                .slsPrcgCndnRecdValidity(SlsPrcgCndnRecdValidity.builder()
                        .conditionValidityStartDate(x.getValidityStartLocalDt())
                        .conditionValidityEndDate(x.getValidityEndLocalDt())
                        .salesOrganization("0511")
                        .distributionChannel("10")
                        .customer(String.valueOf(x.getCustomerId()))
                        .material(String.valueOf(x.getMaterialId()))
                        .build())
                .build()).executeRequest(sapEndpoint.destination);

        mon.complete();
        return new AsyncResult<>(null);

    }

Impact / Priority

Risk of data inconsistency between prices across systems. This could cause legal liabilities or financial losses for us.

Error Message

53273 [async-pool-executor-28] ERROR c.s.c.s.s.d.o.a.ODataVdmEntityAdapter - Could not serialize property 'ConditionApplication'. Returning null instead. 
java.lang.IllegalAccessException: class com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter cannot access a member of class com.sap.cloud.sdk.s4hana.datamodel.odata.namespaces.salespricingconditionrecord.SlsPrcgCndnRecdValidity with modifiers "private"
    at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:385)
    at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:687)
    at java.base/java.lang.reflect.Field.checkAccess(Field.java:1096)
    at java.base/java.lang.reflect.Field.get(Field.java:417)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter.getEntityAsJsonObject(ODataVdmEntityAdapter.java:285)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter.write(ODataVdmEntityAdapter.java:257)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter.write(ODataVdmEntityAdapter.java:47)
    at com.google.gson.TypeAdapter$1.write(TypeAdapter.java:191)
    at com.google.gson.TypeAdapter.toJsonTree(TypeAdapter.java:234)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityListAdapter.write(ODataVdmEntityListAdapter.java:100)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityListAdapter.write(ODataVdmEntityListAdapter.java:27)
    at com.google.gson.TypeAdapter.toJsonTree(TypeAdapter.java:234)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter.getEntityAsJsonObject(ODataVdmEntityAdapter.java:295)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter.write(ODataVdmEntityAdapter.java:257)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter.write(ODataVdmEntityAdapter.java:47)
    at com.google.gson.TypeAdapter$1.write(TypeAdapter.java:191)
    at com.google.gson.Gson.toJson(Gson.java:704)
    at com.google.gson.Gson.toJsonTree(Gson.java:597)
    at com.google.gson.Gson.toJsonTree(Gson.java:576)
    at com.sap.cloud.sdk.datamodel.odata.helper.ODataEntitySerializer.serializeEntityForCreate(ODataEntitySerializer.java:80)
    at com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperCreate.lambda$toRequest$a5827fe7$1(FluentHelperCreate.java:271)
    at io.vavr.control.Try.of(Try.java:75)
    at com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperCreate.toRequest(FluentHelperCreate.java:271)
    at com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperCreate.executeRequest(FluentHelperCreate.java:248)
    at de.ottobock.herakles.priceimporter2.CreationService.saveAsync(CreationService.java:54)
    at de.ottobock.herakles.priceimporter2.CreationService$$FastClassBySpringCGLIB$$4ab10106.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
    at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115)
    at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
    at java.base/java.lang.Thread.run(Thread.java:831)
53273 [async-pool-executor-16] ERROR c.s.c.s.s.d.o.a.ODataVdmEntityAdapter - Could not serialize property 'BRSpcfcTaxDestinationRegion'. Returning null instead. 
java.lang.IllegalAccessException: class com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter cannot access a member of class com.sap.cloud.sdk.s4hana.datamodel.odata.namespaces.salespricingconditionrecord.SlsPrcgCndnRecdValidity with modifiers "private"
    at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:385)
    at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:687)
    at java.base/java.lang.reflect.Field.checkAccess(Field.java:1096)
    at java.base/java.lang.reflect.Field.get(Field.java:417)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter.getEntityAsJsonObject(ODataVdmEntityAdapter.java:285)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter.write(ODataVdmEntityAdapter.java:257)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter.write(ODataVdmEntityAdapter.java:47)
    at com.google.gson.TypeAdapter$1.write(TypeAdapter.java:191)
    at com.google.gson.TypeAdapter.toJsonTree(TypeAdapter.java:234)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityListAdapter.write(ODataVdmEntityListAdapter.java:100)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityListAdapter.write(ODataVdmEntityListAdapter.java:27)
    at com.google.gson.TypeAdapter.toJsonTree(TypeAdapter.java:234)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter.getEntityAsJsonObject(ODataVdmEntityAdapter.java:295)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter.write(ODataVdmEntityAdapter.java:257)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter.write(ODataVdmEntityAdapter.java:47)
    at com.google.gson.TypeAdapter$1.write(TypeAdapter.java:191)
    at com.google.gson.Gson.toJson(Gson.java:704)
    at com.google.gson.Gson.toJsonTree(Gson.java:597)
    at com.google.gson.Gson.toJsonTree(Gson.java:576)
    at com.sap.cloud.sdk.datamodel.odata.helper.ODataEntitySerializer.serializeEntityForCreate(ODataEntitySerializer.java:80)
    at com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperCreate.lambda$toRequest$a5827fe7$1(FluentHelperCreate.java:271)
    at io.vavr.control.Try.of(Try.java:75)
    at com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperCreate.toRequest(FluentHelperCreate.java:271)
    at com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperCreate.executeRequest(FluentHelperCreate.java:248)
    at de.ottobock.herakles.priceimporter2.CreationService.saveAsync(CreationService.java:54)
    at de.ottobock.herakles.priceimporter2.CreationService$$FastClassBySpringCGLIB$$4ab10106.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
    at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115)
    at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
    at java.base/java.lang.Thread.run(Thread.java:831)
53273 [async-pool-executor-19] ERROR c.s.c.s.s.d.o.a.ODataVdmEntityAdapter - Could not serialize property 'Equipment'. Returning null instead. 
java.lang.IllegalAccessException: class com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter cannot access a member of class com.sap.cloud.sdk.s4hana.datamodel.odata.namespaces.salespricingconditionrecord.SlsPrcgCndnRecdValidity with modifiers "private"
    at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:385)
    at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:687)
    at java.base/java.lang.reflect.Field.checkAccess(Field.java:1096)
    at java.base/java.lang.reflect.Field.get(Field.java:417)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter.getEntityAsJsonObject(ODataVdmEntityAdapter.java:285)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter.write(ODataVdmEntityAdapter.java:257)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter.write(ODataVdmEntityAdapter.java:47)
    at com.google.gson.TypeAdapter$1.write(TypeAdapter.java:191)
    at com.google.gson.TypeAdapter.toJsonTree(TypeAdapter.java:234)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityListAdapter.write(ODataVdmEntityListAdapter.java:100)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityListAdapter.write(ODataVdmEntityListAdapter.java:27)
    at com.google.gson.TypeAdapter.toJsonTree(TypeAdapter.java:234)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter.getEntityAsJsonObject(ODataVdmEntityAdapter.java:295)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter.write(ODataVdmEntityAdapter.java:257)
    at com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapter.write(ODataVdmEntityAdapter.java:47)
    at com.google.gson.TypeAdapter$1.write(TypeAdapter.java:191)
    at com.google.gson.Gson.toJson(Gson.java:704)
    at com.google.gson.Gson.toJsonTree(Gson.java:597)
    at com.google.gson.Gson.toJsonTree(Gson.java:576)
    at com.sap.cloud.sdk.datamodel.odata.helper.ODataEntitySerializer.serializeEntityForCreate(ODataEntitySerializer.java:80)
    at com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperCreate.lambda$toRequest$a5827fe7$1(FluentHelperCreate.java:271)
    at io.vavr.control.Try.of(Try.java:75)
    at com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperCreate.toRequest(FluentHelperCreate.java:271)
    at com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperCreate.executeRequest(FluentHelperCreate.java:248)
    at de.ottobock.herakles.priceimporter2.CreationService.saveAsync(CreationService.java:54)
    at de.ottobock.herakles.priceimporter2.CreationService$$FastClassBySpringCGLIB$$4ab10106.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
    at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115)
    at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
    at java.base/java.lang.Thread.run(Thread.java:831)```

Dependency Tree:

[INFO] org.example:price-importer:jar:1.0-SNAPSHOT
[INFO] +- com.sap.cloud.sdk.s4hana:s4hana-api-odata:jar:3.56.0:compile
[INFO] |  +- com.sap.cloud.sdk.cloudplatform:cloudplatform-connectivity:jar:3.56.0:compile
[INFO] |  |  +- com.sap.cloud.sdk.cloudplatform:cloudplatform-core:jar:3.56.0:compile
[INFO] |  |  +- com.sap.cloud.sdk.cloudplatform:caching:jar:3.56.0:compile
[INFO] |  |  +- com.sap.cloud.sdk.cloudplatform:security:jar:3.56.0:compile
[INFO] |  |  +- com.sap.cloud.sdk.cloudplatform:tenant:jar:3.56.0:compile
[INFO] |  |  +- com.sap.cloud.sdk.cloudplatform:servlet:jar:3.56.0:compile
[INFO] |  |  +- com.sap.cloud.sdk.quality:common:jar:3.56.0:compile
[INFO] |  |  |  \- org.apache.commons:commons-csv:jar:1.9.0:compile
[INFO] |  |  +- org.slf4j:jcl-over-slf4j:jar:1.7.32:compile
[INFO] |  |  +- com.github.ben-manes.caffeine:caffeine:jar:2.9.2:compile
[INFO] |  |  +- com.mikesamuel:json-sanitizer:jar:1.2.3:compile
[INFO] |  |  +- org.apache.httpcomponents:httpcore:jar:4.4.14:compile
[INFO] |  |  \- org.apache.commons:commons-lang3:jar:3.12.0:compile
[INFO] |  +- com.sap.cloud.sdk.datamodel:fluent-result:jar:3.56.0:compile
[INFO] |  +- com.sap.cloud.sdk.datamodel:odata-core:jar:3.56.0:compile
[INFO] |  |  +- com.sap.cloud.sdk.datamodel:odata-client:jar:3.56.0:compile
[INFO] |  |  \- joda-time:joda-time:jar:2.10.12:compile
[INFO] |  +- com.sap.cloud.servicesdk:odatav2-connectivity-sdk3:jar:1.40.11:compile
[INFO] |  |  +- commons-io:commons-io:jar:2.6:compile
[INFO] |  |  +- com.sap.cloud.servicesdk:jacksonutil-sdk3:jar:1.40.11:compile
[INFO] |  |  +- org.json:json:jar:20180813:compile
[INFO] |  |  \- com.sap.cloud.servicesdk.prov:api:jar:1.40.11:compile
[INFO] |  |     +- javax.transaction:javax.transaction-api:jar:1.3:compile
[INFO] |  |     +- commons-beanutils:commons-beanutils:jar:1.9.4:compile
[INFO] |  |     |  \- commons-collections:commons-collections:jar:3.2.2:compile
[INFO] |  |     \- com.sap.cds:cds4j-api:jar:1.13.1:compile
[INFO] |  +- com.sap.cloud.servicesdk:odata-v2-lib:jar:1.40.11:compile
[INFO] |  |  \- com.sap.cloud.servicesdk:developer_license:jar:1.40.11:compile
[INFO] |  +- com.google.code.findbugs:jsr305:jar:3.0.2:compile
[INFO] |  +- com.google.code.gson:gson:jar:2.8.8:compile
[INFO] |  +- org.apache.httpcomponents:httpclient:jar:4.5.13:compile
[INFO] |  |  \- commons-codec:commons-codec:jar:1.15:compile
[INFO] |  +- io.vavr:vavr:jar:0.10.4:compile
[INFO] |  |  \- io.vavr:vavr-match:jar:0.10.4:compile
[INFO] |  \- com.google.errorprone:error_prone_annotations:jar:2.9.0:compile
[INFO] +- org.springframework.boot:spring-boot-starter-data-jpa:jar:2.5.5:compile
[INFO] |  +- org.springframework.boot:spring-boot-starter-aop:jar:2.5.5:compile
[INFO] |  |  +- org.springframework:spring-aop:jar:5.3.10:compile
[INFO] |  |  \- org.aspectj:aspectjweaver:jar:1.9.7:compile
[INFO] |  +- org.springframework.boot:spring-boot-starter-jdbc:jar:2.5.5:compile
[INFO] |  |  +- com.zaxxer:HikariCP:jar:4.0.3:compile
[INFO] |  |  \- org.springframework:spring-jdbc:jar:5.3.10:compile
[INFO] |  +- jakarta.transaction:jakarta.transaction-api:jar:1.3.3:compile
[INFO] |  +- jakarta.persistence:jakarta.persistence-api:jar:2.2.3:compile
[INFO] |  +- org.hibernate:hibernate-core:jar:5.4.32.Final:compile
[INFO] |  |  +- org.jboss.logging:jboss-logging:jar:3.4.2.Final:compile
[INFO] |  |  +- org.javassist:javassist:jar:3.27.0-GA:compile
[INFO] |  |  +- net.bytebuddy:byte-buddy:jar:1.10.22:compile
[INFO] |  |  +- antlr:antlr:jar:2.7.7:compile
[INFO] |  |  +- org.jboss:jandex:jar:2.2.3.Final:compile
[INFO] |  |  +- com.fasterxml:classmate:jar:1.5.1:compile
[INFO] |  |  +- org.dom4j:dom4j:jar:2.1.3:compile
[INFO] |  |  +- org.hibernate.common:hibernate-commons-annotations:jar:5.1.2.Final:compile
[INFO] |  |  \- org.glassfish.jaxb:jaxb-runtime:jar:2.3.5:compile
[INFO] |  |     +- org.glassfish.jaxb:txw2:jar:2.3.5:compile
[INFO] |  |     +- com.sun.istack:istack-commons-runtime:jar:3.0.12:compile
[INFO] |  |     \- com.sun.activation:jakarta.activation:jar:1.2.2:runtime
[INFO] |  +- org.springframework.data:spring-data-jpa:jar:2.5.5:compile
[INFO] |  |  +- org.springframework.data:spring-data-commons:jar:2.5.5:compile
[INFO] |  |  +- org.springframework:spring-orm:jar:5.3.10:compile
[INFO] |  |  +- org.springframework:spring-context:jar:5.3.10:compile
[INFO] |  |  |  \- org.springframework:spring-expression:jar:5.3.10:compile
[INFO] |  |  +- org.springframework:spring-tx:jar:5.3.10:compile
[INFO] |  |  \- org.springframework:spring-beans:jar:5.3.10:compile
[INFO] |  \- org.springframework:spring-aspects:jar:5.3.10:compile
[INFO] +- org.slf4j:slf4j-ext:jar:1.7.32:compile
[INFO] |  \- org.slf4j:slf4j-api:jar:1.7.32:compile
[INFO] +- com.microsoft.sqlserver:mssql-jdbc:jar:9.2.1.jre8:runtime
[INFO] +- org.springframework.boot:spring-boot-configuration-processor:jar:2.5.5:compile (optional)
[INFO] +- org.springframework.boot:spring-boot-starter-test:jar:2.5.5:test
[INFO] |  +- org.springframework.boot:spring-boot-starter:jar:2.5.5:compile
[INFO] |  |  +- org.springframework.boot:spring-boot:jar:2.5.5:compile
[INFO] |  |  +- org.springframework.boot:spring-boot-autoconfigure:jar:2.5.5:compile
[INFO] |  |  +- org.springframework.boot:spring-boot-starter-logging:jar:2.5.5:compile
[INFO] |  |  |  +- ch.qos.logback:logback-classic:jar:1.2.6:compile
[INFO] |  |  |  |  \- ch.qos.logback:logback-core:jar:1.2.6:compile
[INFO] |  |  |  +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.14.1:compile
[INFO] |  |  |  |  \- org.apache.logging.log4j:log4j-api:jar:2.14.1:compile
[INFO] |  |  |  \- org.slf4j:jul-to-slf4j:jar:1.7.32:compile
[INFO] |  |  +- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile
[INFO] |  |  \- org.yaml:snakeyaml:jar:1.28:compile
[INFO] |  +- org.springframework.boot:spring-boot-test:jar:2.5.5:test
[INFO] |  +- org.springframework.boot:spring-boot-test-autoconfigure:jar:2.5.5:test
[INFO] |  +- com.jayway.jsonpath:json-path:jar:2.5.0:test
[INFO] |  |  \- net.minidev:json-smart:jar:2.4.7:test
[INFO] |  |     \- net.minidev:accessors-smart:jar:2.4.7:test
[INFO] |  |        \- org.ow2.asm:asm:jar:9.1:test
[INFO] |  +- jakarta.xml.bind:jakarta.xml.bind-api:jar:2.3.3:compile
[INFO] |  |  \- jakarta.activation:jakarta.activation-api:jar:1.2.2:compile
[INFO] |  +- org.assertj:assertj-core:jar:3.19.0:test
[INFO] |  +- org.hamcrest:hamcrest:jar:2.2:test
[INFO] |  +- org.mockito:mockito-core:jar:3.9.0:test
[INFO] |  |  +- net.bytebuddy:byte-buddy-agent:jar:1.10.22:test
[INFO] |  |  \- org.objenesis:objenesis:jar:3.2:test
[INFO] |  +- org.mockito:mockito-junit-jupiter:jar:3.9.0:test
[INFO] |  +- org.skyscreamer:jsonassert:jar:1.5.0:test
[INFO] |  |  \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test
[INFO] |  +- org.springframework:spring-core:jar:5.3.10:compile
[INFO] |  |  \- org.springframework:spring-jcl:jar:5.3.10:compile
[INFO] |  +- org.springframework:spring-test:jar:5.3.10:test
[INFO] |  \- org.xmlunit:xmlunit-core:jar:2.8.2:test
[INFO] +- com.google.guava:guava:jar:30.1.1-jre:compile
[INFO] |  +- com.google.guava:failureaccess:jar:1.0.1:compile
[INFO] |  +- com.google.guava:listenablefuture:jar:9999.0-empty-to-avoid-conflict-with-guava:compile
[INFO] |  +- org.checkerframework:checker-qual:jar:3.8.0:compile
[INFO] |  \- com.google.j2objc:j2objc-annotations:jar:1.3:compile
[INFO] +- com.neovisionaries:nv-i18n:jar:1.29:compile
[INFO] +- org.junit.jupiter:junit-jupiter:jar:5.8.0:test
[INFO] |  +- org.junit.jupiter:junit-jupiter-api:jar:5.7.2:test
[INFO] |  |  +- org.apiguardian:apiguardian-api:jar:1.1.0:test
[INFO] |  |  +- org.opentest4j:opentest4j:jar:1.2.0:test
[INFO] |  |  \- org.junit.platform:junit-platform-commons:jar:1.7.2:test
[INFO] |  +- org.junit.jupiter:junit-jupiter-params:jar:5.7.2:test
[INFO] |  \- org.junit.jupiter:junit-jupiter-engine:jar:5.7.2:test
[INFO] |     \- org.junit.platform:junit-platform-engine:jar:1.7.2:test
[INFO] +- com.github.ozlerhakan:poiji:jar:3.1.1:compile
[INFO] |  \- org.apache.poi:poi-ooxml:jar:5.0.0:compile
[INFO] |     +- org.apache.poi:poi:jar:5.0.0:compile
[INFO] |     |  +- org.apache.commons:commons-collections4:jar:4.4:compile
[INFO] |     |  +- org.apache.commons:commons-math3:jar:3.6.1:compile
[INFO] |     |  \- com.zaxxer:SparseBitSet:jar:1.2:compile
[INFO] |     +- org.apache.poi:poi-ooxml-lite:jar:5.0.0:compile
[INFO] |     |  \- org.apache.xmlbeans:xmlbeans:jar:4.0.0:compile
[INFO] |     +- org.apache.commons:commons-compress:jar:1.20:compile
[INFO] |     +- com.github.virtuald:curvesapi:jar:1.06:compile
[INFO] |     +- org.bouncycastle:bcpkix-jdk15on:jar:1.68:compile
[INFO] |     +- org.bouncycastle:bcprov-jdk15on:jar:1.68:compile
[INFO] |     +- org.apache.santuario:xmlsec:jar:2.2.1:compile
[INFO] |     |  \- com.fasterxml.woodstox:woodstox-core:jar:5.2.1:runtime
[INFO] |     |     \- org.codehaus.woodstox:stax2-api:jar:4.2:runtime
[INFO] |     +- org.apache.xmlgraphics:batik-all:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-anim:jar:1.13:compile
[INFO] |     |  |  +- org.apache.xmlgraphics:batik-shared-resources:jar:1.13:compile
[INFO] |     |  |  \- xml-apis:xml-apis-ext:jar:1.3.04:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-awt-util:jar:1.13:compile
[INFO] |     |  |  \- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.4:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-bridge:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-codec:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-constants:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-css:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-dom:jar:1.13:compile
[INFO] |     |  |  +- xalan:xalan:jar:2.7.2:compile
[INFO] |     |  |  |  \- xalan:serializer:jar:2.7.2:compile
[INFO] |     |  |  \- xml-apis:xml-apis:jar:1.4.01:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-ext:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-extension:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-gui-util:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-gvt:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-i18n:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-parser:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-rasterizer-ext:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-rasterizer:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-script:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-slideshow:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-squiggle-ext:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-squiggle:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-svg-dom:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-svgbrowser:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-svggen:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-svgpp:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-svgrasterizer:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-swing:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-transcoder:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-util:jar:1.13:compile
[INFO] |     |  +- org.apache.xmlgraphics:batik-ttf2svg:jar:1.13:compile
[INFO] |     |  \- org.apache.xmlgraphics:batik-xml:jar:1.13:compile
[INFO] |     \- de.rototor.pdfbox:graphics2d:jar:0.30:compile
[INFO] |        \- org.apache.pdfbox:pdfbox:jar:2.0.22:compile
[INFO] |           \- org.apache.pdfbox:fontbox:jar:2.0.22:compile
[INFO] +- com.squareup.okhttp3:okhttp:jar:4.9.1:compile
[INFO] |  +- com.squareup.okio:okio:jar:2.8.0:compile
[INFO] |  |  \- org.jetbrains.kotlin:kotlin-stdlib-common:jar:1.5.31:compile
[INFO] |  \- org.jetbrains.kotlin:kotlin-stdlib:jar:1.5.31:compile
[INFO] |     \- org.jetbrains:annotations:jar:13.0:compile
[INFO] +- org.projectlombok:lombok:jar:1.18.20:provided
[INFO] +- com.fasterxml.jackson.core:jackson-core:jar:2.12.4:compile
[INFO] +- com.fasterxml.jackson.core:jackson-databind:jar:2.12.4:compile
[INFO] \- com.fasterxml.jackson.core:jackson-annotations:jar:2.12.4:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  18.567 s
[INFO] Finished at: 2021-10-29T13:57:02+02:00
[INFO] ------------------------------------------------------------------------

Project Details

confidential

Checklist

artemkovalyov commented 2 years ago

Hi @newcron, our apologies for the delay in reply. We'll investigate it and get back to shortly.

MatKuhr commented 2 years ago

Hi @newcron, thanks for reaching out!

I checked and I think it can indeed be the case, if the same object is being serialised multiple times at the same time.

The relevant SDK code is probably:

final boolean oldAccessibleValue = propertyField.isAccessible();
propertyField.setAccessible(true);
// this breaks if another thread changes this in the mean time
final Object propertyValue = propertyField.get(value);
propertyField.setAccessible(oldAccessibleValue);

I think we never thought of this case, since typically an object is only sent once to a service, at least at a specific point same time. Can you confirm that this is realistic in your use case? If not, we might have to look further than that..

But of course, this looks like a race condition we need to fix in any case. We'll get back to you with an estimation when a fix will be available.

newcron commented 2 years ago

Yes, I do think it's relevant. I our case, we're updating many prices in parallell. It's not the same instanceof SlsPrcgConditionRecord but for the reflection code you're showing above, you're not working on the object instances, but on the Class object, that all of these instances share.

In that sense, I would argue that indeed there is a good use case to fix thread safetiness on that. Also keep in mind, that when the SAP cloud sdk is used in the context of a web application, there's also a realistic chance of concurrent requests.

newtork commented 2 years ago

Hi @newcron,

While we are comparing different solution to the race condition, I would like to enable you with a workaround until we're done (and released).

I think the current issue boils down to the fact that the parallel threads use the same underlying Gson object. This results into the same ODataVdmEntityAdapterFactory instance being reused in a concurrent environment. The class maintains a collection of stateful java.lang.reflect.Field objects, for which we change accessibility flags. And that leads to the observed race condition.

You could avoid / workaround the problem by making sure every outgoing request has it's own Gson instance in use for JSON serialization. Unfortunately we don't have public API to enable this conveniently. This is why I would now suggest the usage of our Generic OData Client to unblock you:

final SlsPrcgConditionRecord record;

// static data
final String servicePath = SalesPricingConditionRecordService.DEFAULT_SERVICE_PATH;
final String entityCollection = "???" /* LOOK UP VALUE FROM SlsPrcgConditionRecord#getEntityCollection */

// dynamically invoke for every session, alternatively for every outbound request
final HttpClient httpClient = HttpClientAccessor.getHttpClient(sapEndpoint.destination);

// dynamically invoke for every outbound request
final Gson gson = new GsonBuilder().create();
final String payload = gson.toJson(record);
final ODataResourcePath resourcePath = ODataResourcePath.of(entityCollection);
final ODataRequestCreate request = new ODataRequestCreate(servicePath, resourcePath, payload, ODataProtocol.V2);
request.execute(httpClient); // response can be evaluated if necessary

Please let me know if it works for you.

MatKuhr commented 2 years ago

Update: A fix for this has been shipped with Cloud SDK Version 3.59.0.