You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 45 Next »

The goal of this document is to investigate about the PostgreSQL capability to handle Json documents.

Introduction

PostgreSQL offers two types for storing JSON data: json and jsonb. The json data type stores an exact copy of the input text, which processing functions must reparse on each execution; while jsonb data is stored in a decomposed binary format that makes it slightly slower to input due to added conversion overhead, but significantly faster to process, since no reparsing is needed. jsonb also supports indexing, which can be a significant advantage.

Because the json type stores an exact copy of the input text, it will preserve semantically-insignificant white space between tokens, as well as the order of keys within JSON objects. Also, if a JSON object within the value contains the same key more than once, all the key/value pairs are kept. (The processing functions consider the last value as the operative one.) By contrast, jsonb does not preserve white space, does not preserve the order of object keys, and does not keep duplicate object keys. If duplicate keys are specified in the input, only the last value is kept.

Note

Using PostgreSQL special type as "json" or "jsonb" rather than "text", all applications will lose any compatibility with H2 and MariaDB. They will be mentioned in this document but not recommended.

TypeDataStoreValidationSupport QueryIndexPreserve Order
longtext

MariaDBSELECTYesNoYes
PostgreSQLNoNoNoYes
jsonPostgreSQLINSERT / UPDATEYesNoYes
jsonbPostgreSQLINSERT / UPDATEYesYesNo

Requirements

  • Policy types/Policies/Node Types/Node Templates are first order items
  • Data Types have a scope of a first order item, so a data type definition only applies in the scope of a policy type or node type definition
  • We should keep our current APIs, all changes should be internal
  • We must provide an upgrade path to the new data structure and a rollback to the current structure

ORM Layer using Document Storage

ORM layer using document storage (PostgreSQL or MongoDB) could be organized in two layer:

  • Document layer (Domain Model to be converted in Json) - implementation has no dependency from DB
  • Persistence layer - (Domain Model depend of the DB used): Entities for PostgreSQL, Documents for MongoDB

An implementation on the Document layer could be found here: (https://gerrit.nordix.org/c/onap/policy/models/+/13633)

Example

In the example below DocToscaServiceTemplate should be serialized to Json.

Persistence Model
@Data
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
public class DocToscaServiceTemplate extends DocToscaEntity<ToscaServiceTemplate> {

    @SerializedName("data_types")
    private Map<String, DocToscaDataType> dataTypes;

    -------

   public DocToscaServiceTemplate(ToscaServiceTemplate authorativeConcept) {
        this.fromAuthorative(authorativeConcept);
    }

    @Override
    public ToscaServiceTemplate toAuthorative() {
        final var toscaServiceTemplate = new ToscaServiceTemplate();
        super.setToscaEntity(toscaServiceTemplate);
        super.toAuthorative();

        if (dataTypes != null) {
            toscaServiceTemplate.setDataTypes(PfUtils.mapMap(dataTypes, DocToscaDataType::toAuthorative));
        }
     -------
 
        return toscaServiceTemplate;
    }

    @Override
    public void fromAuthorative(ToscaServiceTemplate toscaServiceTemplate) {
        super.fromAuthorative(toscaServiceTemplate);
 
        if (toscaServiceTemplate.getDataTypes() != null) {
            dataTypes = DocUtil.mapMap(toscaServiceTemplate.getDataTypes(), DocToscaDataType::new);
        }
 
        ------- 
    }
}


In the example below the implementation of JpaToscaServiceTemplate for PostgreSQL/MariaDB

Persistence Model
@Entity
@Table(name = "ToscaServiceTemplate")
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@Data
@EqualsAndHashCode(callSuper = false)
public class JpaToscaServiceTemplate extends PfConcept implements PfAuthorative<ToscaServiceTemplate> {

    @EmbeddedId
    @VerifyKey
    @NotNull
    private PfConceptKey key;

    @Lob
    @Convert(converter = StringToServiceTemplateConverter.class)
    @NotNull
    private DocToscaServiceTemplate serviceTemplate;

     ------- 
 
    @Override
    public ToscaServiceTemplate toAuthorative() {
        return serviceTemplate.toAuthorative();
    }

    @Override
    public void fromAuthorative(ToscaServiceTemplate authorativeConcept) {
        serviceTemplate = new DocToscaServiceTemplate(authorativeConcept);
        setKey(serviceTemplate.getKey().asIdentifier().asConceptKey());
    }
         ------- 
 }


In the example below the implementation of JpaToscaServiceTemplate for MongoDB

Persistence Model
@Document(collection = "ToscaServiceTemplate")
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@Data
@EqualsAndHashCode(callSuper = false)
public class JpaToscaServiceTemplate extends PfConcept implements PfAuthorative<ToscaServiceTemplate> {

    @Id
    @VerifyKey
    @NonNull
    private PfConceptKey key;

    @NonNull
    private DocToscaServiceTemplate serviceTemplate;

     ------- 
 
    @Override
    public ToscaServiceTemplate toAuthorative() {
        return serviceTemplate.toAuthorative();
    }

    @Override
    public void fromAuthorative(ToscaServiceTemplate authorativeConcept) {
        serviceTemplate = new DocToscaServiceTemplate(authorativeConcept);
        setKey(serviceTemplate.getKey().asIdentifier().asConceptKey());
    }
         ------- 
 }

Converters

Jakarta and Spring do no support json Type, but we can use Converters to convert DocToscaServiceTemplate  to a Json String.

Converters
@Converter(autoApply = true)
public class StringToServiceTemplateConverter implements AttributeConverter<DocToscaServiceTemplate, String> {

    private static final Coder coder = new StandardCoder();

    @Override
    public String convertToDatabaseColumn(DocToscaServiceTemplate serviceTemplate) {
        try {
            return serviceTemplate == null ? null : coder.encode(serviceTemplate);
        } catch (CoderException e) {
            throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, e.getMessage(), e);
        }
    }

    @Override
    public DocToscaServiceTemplate convertToEntityAttribute(String dbData) {
        if (dbData == null) {
            return new DocToscaServiceTemplate();
        }
        try {
            return coder.decode(dbData, DocToscaServiceTemplate.class);
        } catch (CoderException e) {
            throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, e.getMessage(), e);
        }
    }
}

Note: Serialization and deserialization in Json is already used in policy-model (Un example could be found here [JpaToscaPolicy.java]). @Converter is just an elegant way to do the same thing.

ToscaServiceTemplate Table

clampacm=# \d ToscaServiceTemplate
                    Table "public.toscaservicetemplate"
     Column      |          Type          | Collation | Nullable | Default
-----------------+------------------------+-----------+----------+---------
 name            | character varying(120) |           | not null |
 version         | character varying(20)  |           | not null |
 servicetemplate | text                   |           |          |
Indexes:
    "toscaservicetemplate_pkey" PRIMARY KEY, btree (name, version)


clampacm=# select * from public.ToscaServiceTemplate;
        name        | version | servicetemplate
--------------------+---------+-----------------
 PMSH_Test_Instance | 1.0.0   | 16505
(1 row)


clampacm=# select convert_from(lo_get(servicetemplate::oid), 'UTF8') from toscaservicetemplate;
{"tosca_definitions_version":"tosca_simple_yaml_1_3", ...

Proposals

The new ORM level should be additional and optional to the existence one. My propose options are shown below:

  1. Save ToscaServiceTemplace as Json String in a single Entity:
    • ToscaServiceTemplace saved in a Text field as Json
    • It is compatible with H2, MariaDB and PostgreSQL
    • It is an additional code for policy-models and a medium impact for all applications that are using it
    • That solution is compatible with not Spring Applications
  2. MongoDB/Cassandra
    • Document oriented approach full supported by SpringBoot (not needs Converters)
    • Compatible only with MongoDB/Cassandra (MongoDB and Cassandra are not compatible to each other)
    • It is an additional code for policy-models and a huge impact for all applications that are using it (all repositories and persistence classes have to change to a Document oriented classes)
    • Unit tests need an Embedded Server (example for cassandra: EmbeddedCassandraServerHelper or CassandraContainer)
    • Spring Boot has own annotations for Documents. Eventually for application not in Spring Boot, it needs additional dao style implementation

Note

  • Using document storage, it involves only the ORM layer, it does not change the functionality of the application
  • After migration to document storage as ORM layer,  it will possible to adjust flexibility of Tosca Service Template Handling (POLICY-3236); as new feature it will impact the business logic of the application

Benchmark Performance of runtime-acm

Work in progress ....

In order to generate the benchmark I have used:

  • Jmeter to generate requests (same used by performance tests): Requests/sec: 287
  • Prometheus for monitoring
  • DMaap simulator
  • Participant simulator
  • MariaDB/PostgreSQL/MongoDB

The existing system

Eclipse-Link/Mariadb. Tosca Service template is saved as a schema entity relation.

Postgres with the JSON store

Hibernate/PostgreSQL. Tosca Service Template is saved into a jsonb type.

The equivalent in MariaDB

Hibernate/Mariadb. Tosca Service Template is saved into a longtext as Json.

MongoDB

MongoDB. Tosca Service Template is saved as MongoDB Document.

  • id cannot have dot '.' in MongoDB : solved with minimal configuration

Benchmark Performance of Policy Pap

In order to generate the benchmark I have used:

  • Jmeter to generate requests (same used by performance tests)
  • Prometheus for monitoring
  • DMaap simulator
  • MariaDB

The existing system

Eclipse-Link/Mariadb with dao pattern and atomic transactions. At that time the migration to Spring repositories are not finished yet, so the benchmark is calculate by endpoints performance ('server_requests' metric).


Conclusion

  • Moving to Spring Data JPA there are already improvements
  • NoSql does not have standard, so PostgreSQL jsonb, MongoDB and Cassandra are not compatible to each other
  • There is much more support for MongoDB/Cassandra than PostgreSQL jsonb
  • In MongoDB/Cassandra JSON queries are much more intuitive than PostgreSQL jsonb
  • PostgreSql seems to be the fastest


  • No labels