You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@ignite.apache.org by "Ivan Bessonov (Jira)" <ji...@apache.org> on 2021/09/22 10:17:00 UTC

[jira] [Updated] (IGNITE-15557) [Ignite 3] Replace "changers" with "pojos" as a default mean to work with configurations

     [ https://issues.apache.org/jira/browse/IGNITE-15557?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Ivan Bessonov updated IGNITE-15557:
-----------------------------------
    Description: 
Using lambda expressions to change Ignite configuration values proved to be inconvenient to many developers. Not only that, but using lambdas in Java thin client looks out of place and creates technical difficulties of properly serializing them. There's a request to use POJO classes, possibly with Builders associated to them.
h3. New API

There's a number of ways one can approach the design of this API. I see two main options:
 * good old pojo-based fluent interfaces:

{code:java}
ignite().nodeConfiguration()
    .setNetworkConfiguration(new NetworkConfig()
        .setServer(new ServerConfig()
            .setPortRange(1000)
        )
        .setNodeFinder(new new StaticNodeFinderConfig() // polymorphic
            .setNetClusterNodes("localhost")
        )
    );{code}
 * same thing, but with "build()" at the end of each instance creation. Personally, I think it's a bit excessive here.

Somewhat challenging here is a notion of "named lists". I propose the following solution: 
{code:java}
new TableConfig()
    .addColumn(new ColumnConfiguration("col1").setType(INT32))
    .addColumn(new ColumnConfiguration("col2").setType(INT64))
// just puts null in the collection. Same as in HOCON notation
    .removeColumn("col3");{code}
Also, same as HOCON notation, there's no API to manipulate ordering of elements. That's the internal stuff. Instances, passed into new "set" methods, should be treated if they represent a JSON object.
h3. Serialization

Contrary to what just have been written, named list elements should be processed in the same order as they were added into an object, at least that's a desirable feature. "LinkedHashMap" allows us to achieve this, but then it must be properly serialized in thin protocol. The problem is that one can't just create JSON tree out of the object and then convert it back - JSON does not preserve ordering of the elements.

Default Java serialization can solve the problem, but interoperability with other languages will suffer. Custom serialization format should probably be used or we should just treat the object as "OrderedMap<String, Either<OrderedMap, ?/* leaf values */>>. This would make the most sense, we just have to make sure that "the other side" reads it into "LinkedHashMap", not the plain one.
h3. Changers API should be hidden

"org.apache.ignite.configuration.ConfigurationTree#change" method should be hidden somewhere. Maybe in "DynamicConfiguration", that's not very important.

What's important is this:
 * ConfigurationTree should have method "update" or "set" that accepts POJO;
 * "change" should be removed;
 * There should be a method somewhere like this:

{code:java}
public static <Change> Future<Void> change(
    ConfigurationTree<?, Change, ?> cfg,
    Consumer<Change> change
) { ... }{code}
and it'll do all the magic. Of course, it should be located somewhere in internal classes.
h3. Node start API

It would actually be convenient to start nodes in embedded mode with the ability to pass configuration as an object, not as a text file. For tests at least. So builder like this would be welcomed in my opinion:
{code:java}
Ignite ignite = Ignition.serverNode(name, workDir)
    .withNodeFinder(...)
    .withNetwork(...)
    .start();{code}
h3. Implementation notes

Code generation in annotation processor is obviously required. I consider this a simple part.

What's less obvious is a way to convert POJO into "changer" closure in the internals of configuration framework.
 * Option one. Each POJO object will provide a single magic method to convert itself into a prefix tree. This is the simplest solution one can come up with but it pollutes the interface of configuration objects.
 * Option two. Generate runtime converters that use var handles or something as fancy. Not too hard to implement, no separate methods are required in objects. Con - same conversion logic would have to be implemented both in thin client and in server, that's not very good.

I lean towards to option 1 as the simplest.

  was:
Using lambda expressions to change Ignite configuration values proved to be inconvenient to many developers. Not only that, but using lambdas in Java thin client looks out of place and creates technical difficulties of properly serializing them. There's a request to use POJO classes, possibly with Builders associated to them.
h3. New API

There's a number of ways one can approach the design of this API. I see two main options:
 * good old pojo-based fluent interfaces:

{code:java}
ignite().nodeConfiguration()
    .setNetworkConfiguration(new NetworkConfig()
        .setServer(new ServerConfig()
            .setPortRange(1000)
        )
        .setNodeFinder(new new StaticNodeFinderConfig() // polymorphic
            .setNetClusterNodes("localhost")
        )
    );{code}

 * same thing, but with "build()" at the end of each instance creation. Personally, I think it's a bit excessive here.

Somewhat challenging here is a notion of "named lists". I propose the following solution:

 
{code:java}
new TableConfig()
    .addColumn(new ColumnConfiguration("col1").setType(INT32))
    .addColumn(new ColumnConfiguration("col2").setType(INT64))
// just puts null in the collection. Same as in HOCON notation
    .removeColumn("col3");{code}
Also, same as HOCON notation, there's no API to manipulate ordering of elements. That's the internal stuff. Instances, passed into new "set" methods, should be treated if they represent a JSON object.
h3. Serialization

Contrary to what just have been written, named list elements should be processed in the same order as they were added into an object, at least that's a desirable feature. "LinkedHashMap" allows us to achieve this, but then it must be properly serialized in thin protocol. The problem is that one can't just create JSON tree out of the object and then convert it back - JSON does not preserve ordering of the elements.

Default Java serialization can solve the problem, but interoperability with other languages will suffer. Custom serialization format should probably be used or we should just treat the object as "OrderedMap<String, Either<OrderedMap, ?/* leaf values */>>. This would make the most sense, we just have to make sure that "the other side" reads it into "LinkedHashMap", not the plain one.
h3. Changers API should be hidden

"org.apache.ignite.configuration.ConfigurationTree#change" method should be hidden somewhere. Maybe in "DynamicConfiguration", that's not very important.

What's important is this:
 * ConfigurationTree should have method "update" or "set" that accepts POJO;
 * "change" should be removed;
 * There should be a method somewhere like this:

{code:java}
public static <Change> Future<Void> change(
    ConfigurationTree<?, Change, ?> cfg,
    Consumer<Change> change
) { ... }{code}
and it'll do all the magic. Of course, it should be located somewhere in internal classes.

h3. Node start API

It would actually be convenient to start nodes in embedded mode with the ability to pass configuration as an object, not as a text file. For tests at least. So builder like this would be welcomed in my opinion:
{code:java}
Ignite ignite = Ignition.serverNode(name, workDir)
    .withNodeFinder(...)
    .withNetwork(...)
    .start();{code}
h3. Implementation notes

Code generation in annotation processor is obviously required. I consider this a simple part.

What's less obvious is a way to convert POJO into "changer" closure in the internals of configuration framework.
 * Option one. Each POJO object will provide a single magic method to convert itself into a prefix tree. This is the simplest solution one can come up with but it pollutes the interface of configuration objects.
 * Option two. Generate runtime converters that use var handles or something as fancy. Not too hard to implement, no separate methods are required in objects. Con - same conversion logic would have to be implemented both in thin client and in server, that's not very good.

I lean towards to option 1 as the simplest.


> [Ignite 3] Replace "changers" with "pojos" as a default mean to work with configurations
> ----------------------------------------------------------------------------------------
>
>                 Key: IGNITE-15557
>                 URL: https://issues.apache.org/jira/browse/IGNITE-15557
>             Project: Ignite
>          Issue Type: Improvement
>    Affects Versions: 3.0.0-alpha2
>            Reporter: Ivan Bessonov
>            Priority: Major
>              Labels: iep-55, ignite-3
>
> Using lambda expressions to change Ignite configuration values proved to be inconvenient to many developers. Not only that, but using lambdas in Java thin client looks out of place and creates technical difficulties of properly serializing them. There's a request to use POJO classes, possibly with Builders associated to them.
> h3. New API
> There's a number of ways one can approach the design of this API. I see two main options:
>  * good old pojo-based fluent interfaces:
> {code:java}
> ignite().nodeConfiguration()
>     .setNetworkConfiguration(new NetworkConfig()
>         .setServer(new ServerConfig()
>             .setPortRange(1000)
>         )
>         .setNodeFinder(new new StaticNodeFinderConfig() // polymorphic
>             .setNetClusterNodes("localhost")
>         )
>     );{code}
>  * same thing, but with "build()" at the end of each instance creation. Personally, I think it's a bit excessive here.
> Somewhat challenging here is a notion of "named lists". I propose the following solution: 
> {code:java}
> new TableConfig()
>     .addColumn(new ColumnConfiguration("col1").setType(INT32))
>     .addColumn(new ColumnConfiguration("col2").setType(INT64))
> // just puts null in the collection. Same as in HOCON notation
>     .removeColumn("col3");{code}
> Also, same as HOCON notation, there's no API to manipulate ordering of elements. That's the internal stuff. Instances, passed into new "set" methods, should be treated if they represent a JSON object.
> h3. Serialization
> Contrary to what just have been written, named list elements should be processed in the same order as they were added into an object, at least that's a desirable feature. "LinkedHashMap" allows us to achieve this, but then it must be properly serialized in thin protocol. The problem is that one can't just create JSON tree out of the object and then convert it back - JSON does not preserve ordering of the elements.
> Default Java serialization can solve the problem, but interoperability with other languages will suffer. Custom serialization format should probably be used or we should just treat the object as "OrderedMap<String, Either<OrderedMap, ?/* leaf values */>>. This would make the most sense, we just have to make sure that "the other side" reads it into "LinkedHashMap", not the plain one.
> h3. Changers API should be hidden
> "org.apache.ignite.configuration.ConfigurationTree#change" method should be hidden somewhere. Maybe in "DynamicConfiguration", that's not very important.
> What's important is this:
>  * ConfigurationTree should have method "update" or "set" that accepts POJO;
>  * "change" should be removed;
>  * There should be a method somewhere like this:
> {code:java}
> public static <Change> Future<Void> change(
>     ConfigurationTree<?, Change, ?> cfg,
>     Consumer<Change> change
> ) { ... }{code}
> and it'll do all the magic. Of course, it should be located somewhere in internal classes.
> h3. Node start API
> It would actually be convenient to start nodes in embedded mode with the ability to pass configuration as an object, not as a text file. For tests at least. So builder like this would be welcomed in my opinion:
> {code:java}
> Ignite ignite = Ignition.serverNode(name, workDir)
>     .withNodeFinder(...)
>     .withNetwork(...)
>     .start();{code}
> h3. Implementation notes
> Code generation in annotation processor is obviously required. I consider this a simple part.
> What's less obvious is a way to convert POJO into "changer" closure in the internals of configuration framework.
>  * Option one. Each POJO object will provide a single magic method to convert itself into a prefix tree. This is the simplest solution one can come up with but it pollutes the interface of configuration objects.
>  * Option two. Generate runtime converters that use var handles or something as fancy. Not too hard to implement, no separate methods are required in objects. Con - same conversion logic would have to be implemented both in thin client and in server, that's not very good.
> I lean towards to option 1 as the simplest.



--
This message was sent by Atlassian Jira
(v8.3.4#803005)