Hello, Pavel. I support your proposal to transparently create custom AffinityMapper based on server node classname, in general. Partition Awareness crucial point for a thin client performance so It should be absolutely clear for a user - PA works correctly or not.
So, I think, we should implement the following: 0. Add ability to sent custom affinity class name. 1. If `partitionAwarenessEnabled=true` but custom affinity can’t be instantiated on the client - throw an exception. WDYT? > 8 июля 2022 г., в 08:37, Pavel Tupitsyn <ptupit...@apache.org> написал(а): > > To clarify: you can use a different AffinityFunction implementation on the > client side. It just has to map to the same binary typeId. > Even if there is a legacy setup with AffinityKeyMapper on servers, you can > craft an AffinityFunction for the client that achieves correct behavior. > So I believe this should cover any use case. > > On Thu, Jul 7, 2022 at 10:36 PM Pavel Tupitsyn <ptupit...@apache.org> wrote: > >> AffinityKeyMapper is just a subset of AffinityFunction, isn't it? >> So by supporting AffinityFunction we cover all AffinityKeyMapper use cases. >> >> >>> managing the classpath, a user should always keep in mind it >> I don't consider this a problem. This is how Java works, what can we do? >> You can also stick the class into BinaryConfiguration on the client to >> make it explicit and make sure it is loaded. >> >>> it still possible having user bad implementations of AffinityFunction >> that can't be easily instantiated; >> It is also possible to provide a bad implementation of ToIntFunction, no >> difference. >> >>> a question - what we should do in case of class instantiating loading >> fails? >> Currently we don't fail on custom affinity, right? So we should keep this >> behavior. Print a warning. >> >> On Thu, Jul 7, 2022 at 10:27 PM Maxim Muzafarov <mmu...@apache.org> wrote: >> >>> Pavel, >>> >>> >>> Yes, you're right that AffinityKeyMapper is deprecated, this is sad to >>> say but it still used. >>> >>> Let me describe the cases in more detail. In general, we have >>> customers which are using: >>> >>> 1. an own custom AffinityFunction; >>> 2. the default RendezvousAffinityFunction + an own deprecated >>> AffinityKeyMapper; >>> 3. an own custom AffinityFunction + an own deprecated >>> AffinityKeyMapper (I doubt about this case); >>> >>> I'd like to provide a general solution that solves all the cases >>> mentioned above, thus we can't discuss only AffinityFunction. It >>> doesn't fit the initial goals. >>> >>> >>>> Can you please clarify the "tricky" part? >>> >>> Yes, from my point of view the tricky part here is: >>> >>> - managing the classpath, a user should always keep in mind it; >>> - it still possible having user bad implementations of >>> AffinityFunction that can't be easily instantiated; >>> - a question - what we should do in case of class instantiating >>> loading fails? drop the client, print the warn or do nothing. These >>> solutions have a different impact on a production environment and user >>> may have different expectations on that; >>> >>> >>> I'm not saying that solution you suggested is bad, it just has some >>> drawbacks (like other solutions) and doesn't solves all the cases if >>> we're not instantiating a deprecated AffinityKeyMapper (which looks a >>> bit ugly for me too). >>> >>> >>> Actually, we are not limited with setting/instantiating >>> AffinityFunction. We can provide for a user a control on which channel >>> (node) a particular cache request will be executed (this thing also >>> have a drawback - a transactional operation cannot be executed on an >>> arbitrary node, it should be executed on node it's started). >>> >>> On Thu, 7 Jul 2022 at 21:41, Pavel Tupitsyn <ptupit...@apache.org> wrote: >>>> >>>>> AffinityKeyMapper (it will be required too) >>>> It is deprecated. We should not add any functionality on top of >>> deprecated >>>> stuff. >>>> Let's discuss only AffinityFunction. >>>> >>>>> this approach requires of these classes to be present on a thin client >>>> side and it can be tricky >>>> Any of the approaches discussed above requires some additional logic to >>> be >>>> present on the client. >>>> Can you please clarify the "tricky" part? >>>> >>>> All you need is to provide AffinityFunction implementation with a proper >>>> class name >>>> (or even use nameMapper/idMapper to map to a different name), it is no >>>> different from the ToIntFunction approach. >>>> >>>> >>>> On Thu, Jul 7, 2022 at 9:23 PM Maxim Muzafarov <mmu...@apache.org> >>> wrote: >>>> >>>>> Folks, >>>>> >>>>> I'd like also to mention since we are talking about a customer's >>>>> custom affinity function and affinity mapper implementations we could >>>>> face with some issues during these classes instantiation. It could >>>>> work for the customer which I've faced on, but I cannoun be sure that >>>>> this approach will work for all of the cases. >>>>> >>>>> I think it is better to avoid such an issues and leave it up to a >>>>> user. Nevertheless, the worst scenario for the user would be having 2 >>>>> hops on each put/get as it was before this thread. >>>>> >>>>> On Thu, 7 Jul 2022 at 21:12, Maxim Muzafarov <mmu...@apache.org> >>> wrote: >>>>>> >>>>>> Folks, >>>>>> >>>>>> I'm not familiar with a thin client protocol, so, please, correct me >>>>>> if I'm wrong - is it possible sending classes over the network for >>>>>> different type of thin clients? It seems to me it will not work. I >>>>>> think it is possible to use class name/or type id and we are capable >>>>>> to instantiate both of the AffinityFunction and AffinityKeyMapper >>> (it >>>>>> will be required too) classes. However, this approach requires of >>>>>> these classes to be present on a thin client side and it can be >>> tricky >>>>>> to configure if this client is used inside a container (e.g. with >>>>>> spring data or something). >>>>>> >>>>>> Setting directly some kind of a known mapper looks more >>>>>> straightforward and error prone approach. >>>>>> >>>>>> Please, correct me if I am wrong. >>>>>> >>>>>> On Thu, 7 Jul 2022 at 21:02, Семён Данилов <samvi...@yandex.ru> >>> wrote: >>>>>>> >>>>>>> +1 to Pavel’s approach. Giving user the ability to set affinity >>>>> function that is potentially different from the one that is on server >>>>> doesn’t look good. >>>>>>> >>>>>>> I’d even go further and try sending the affinity function class >>> itself >>>>> over the network, so users won’t have to alter the classpath by >>> adding the >>>>> class that is not directly used in the codebase. >>>>>>> On 7 Jul 2022, 21:41 +0400, Pavel Tupitsyn <ptupit...@apache.org >>>> , >>>>> wrote: >>>>>>>> Can we actually avoid any public API changes and do the >>> following: >>>>>>>> >>>>>>>> When a feature flag is present and there is a custom affinity >>>>> function on >>>>>>>> the server, send back the class name (or binary type id, if >>>>> possible) of >>>>>>>> the affinity function? >>>>>>>> Then simply instantiate it on the client and call >>> `partition(Object >>>>> key)` >>>>>>>> on it? >>>>>>>> >>>>>>>> On Thu, Jul 7, 2022 at 8:32 PM Maxim Muzafarov < >>> mmu...@apache.org> >>>>> wrote: >>>>>>>> >>>>>>>>> Pavel, >>>>>>>>> >>>>>>>>> Here is my thoughts. >>>>>>>>> >>>>>>>>> As I mentioned before, we will need only the >>>>>>>>> AffinityFunction#partition(Object key) method for calculation >>> on >>>>> the >>>>>>>>> client side due to all the partition-to-node mappings will be >>>>>>>>> requested asynchronously from a server node how it is >>> happening now >>>>>>>>> for cache groups with the default affinity function. Thus >>> setting >>>>> the >>>>>>>>> AffinityFunction looks on the client side redundant and it >>> can be >>>>>>>>> transformed to a simple ToInFunction<Object> interface. >>>>>>>>> >>>>>>>>> public interface ToIntFunction<Object> { >>>>>>>>> int applyAsInt(Object key); >>>>>>>>> } >>>>>>>>> >>>>>>>>> Another point that I'd like also to mention is that the cache >>>>> already >>>>>>>>> created in a cluster, so it will be better to eliminate the >>>>> duplicate >>>>>>>>> affinity function configuration (setting the number of >>>>> partitions). It >>>>>>>>> is fully possible to receive the number of partitions from a >>> server >>>>>>>>> node (the same way as it currently happening). Thus instead >>> of the >>>>>>>>> ToInFunction<Object> interface it will be better to use >>>>>>>>> ToInBiFunction<Object, Integer>. >>>>>>>>> >>>>>>>>> public interface ToIntBiFunction<Object, Integer> { >>>>>>>>> int applyAsInt(Object key, Integer parts); >>>>>>>>> } >>>>>>>>> >>>>>>>>> >>>>>>>>> I agree with you that "setPartitionAwarenessKeyMapper" may be >>> is >>>>> not >>>>>>>>> good naming here, we can rename it and move it to the public >>> API, >>>>> of >>>>>>>>> course. >>>>>>>>> >>>>>>>>> On Thu, 7 Jul 2022 at 20:06, Pavel Tupitsyn < >>> ptupit...@apache.org> >>>>> wrote: >>>>>>>>>> >>>>>>>>>> Maxim, >>>>>>>>>> >>>>>>>>>> I am confused. We were talking about a custom Affinity >>> Function. >>>>>>>>>> As you noted, AffinityKeyMapper is deprecated, why do we add >>>>> something >>>>>>>>>> named "setPartitionAwarenessKeyMapper"? >>>>>>>>>> >>>>>>>>>> Internal API approach is hacky. >>>>>>>>>> IMO we should either develop a proper feature with a good >>> public >>>>> API, or >>>>>>>>>> not add anything at all. >>>>>>>>>> >>>>>>>>>> On Thu, Jul 7, 2022 at 6:34 PM Maxim Muzafarov < >>>>> mmu...@apache.org> >>>>>>>>> wrote: >>>>>>>>>> >>>>>>>>>>> Folks, >>>>>>>>>>> >>>>>>>>>>> >>>>>>>>>>> Thank you for your comments. >>>>>>>>>>> >>>>>>>>>>> First of all, I'd like to point out that if the cache is >>>>> created with >>>>>>>>>>> a custom affinity function or the legacy AffinityKeyMapper >>>>> interface, >>>>>>>>>>> the thin client should be able to provide only a >>>>> `key-to-partition` >>>>>>>>>>> mapping function to handle the case described above. The >>>>>>>>>>> `partition-to-node` mappings the client will receive from >>> a >>>>> server >>>>>>>>>>> node it connected to. This will simplify a bit the final >>>>> solution. >>>>>>>>>>> >>>>>>>>>>> ================== >>>>>>>>>>> >>>>>>>>>>> I've checked your suggestions and it looks like both of >>> them >>>>> have some >>>>>>>>>>> sufficient drawbacks: >>>>>>>>>>> >>>>>>>>>>> 1. Using SystemProperty looks really hacky and we are >>>>> explicitly >>>>>>>>>>> complicate a thin client configuration for an end user. >>>>>>>>>>> >>>>>>>>>>> 2. Extending the ClientCacheConfiguration is a very good >>> and >>>>>>>>>>> straightforward idea, however it doesn't fit the case >>>>> described above. >>>>>>>>>>> Caches previously created with custom affinity >>> functions/key >>>>> mappers >>>>>>>>>>> already present in the cluster, so in this case we are >>> forcing >>>>> a user >>>>>>>>>>> to store an additional ClientCacheConfiguration. This is >>> not >>>>> good. The >>>>>>>>>>> cache.getOrCreate(cfg) method will also be used and fire >>> in >>>>> turn >>>>>>>>>>> CACHE_GET_OR_CREATE_WITH_CONFIGURATION request which is >>> not >>>>> necessary >>>>>>>>>>> here. For this case using cache.name(str) is only enough. >>>>>>>>>>> >>>>>>>>>>> ================== >>>>>>>>>>> >>>>>>>>>>> I propose the following two solutions that looks very >>>>> promising: >>>>>>>>>>> >>>>>>>>>>> 3. Extending cache create methdos with a >>> ClientCacheContext in >>>>> the >>>>>>>>>>> IgniteClient interface. This context will contain all >>>>> additional cache >>>>>>>>>>> attributes like custom cache affinity mappers that map >>> cache >>>>> keys to >>>>>>>>>>> partitions if a custom affinity was used on the server >>> side >>>>> (note that >>>>>>>>>>> all partition-to-node mappings will be received by thin >>> client >>>>> from a >>>>>>>>>>> server node). >>>>>>>>>>> >>>>>>>>>>> interface IgniteClientEx extends IgniteClient { >>>>>>>>>>> ClientCache<K, V> name(String name, ClientCacheContext >>> cctx); >>>>>>>>>>> ClientCache<K, V> getOrCreateCache(String name, >>>>> ClientCacheContext >>>>>>>>>>> cctx); >>>>>>>>>>> ClientCache<K, V> >>> getOrCreateCache(ClientCacheConfiguration >>>>> cfg, >>>>>>>>>>> ClientCacheContext cctx); >>>>>>>>>>> } >>>>>>>>>>> >>>>>>>>>>> class ClientCacheContext { >>>>>>>>>>> setPartitionAwarenessKeyMapper(ToIntBiFunction<Object, >>> Integer> >>>>>>>>>>> mapper); >>>>>>>>>>> } >>>>>>>>>>> >>>>>>>>>>> 4. Use the same approach as the IgniteCache interface >>> does for >>>>> the >>>>>>>>>>> same things - adding withPartitionAwarenessKeyMapper() to >>> the >>>>>>>>>>> interface. This method will allow to configure the thin >>> client >>>>>>>>>>> execution behaviour for the partition awareness feature by >>>>> setting a >>>>>>>>>>> custom cache key mapper. >>>>>>>>>>> >>>>>>>>>>> ================== >>>>>>>>>>> >>>>>>>>>>> I've used the 4-th solution due to it brings much less >>> source >>>>> code to >>>>>>>>>>> the Apache Ignite codebase and looks a bit simpler to >>>>> configure for a >>>>>>>>>>> user. I've also move the withPartitionAwarenessKeyMapper() >>>>> method to >>>>>>>>>>> an internal API interface which still solves a user issue >>> with >>>>> the >>>>>>>>>>> partition awareness, but also specifies that the custom >>> mapping >>>>>>>>>>> function and deprecated AffinityKeyMapper should not be >>> used, >>>>> in >>>>>>>>>>> general. >>>>>>>>>>> >>>>>>>>>>> Please, take a look at my patch: >>>>>>>>>>> >>>>>>>>>>> https://issues.apache.org/jira/browse/IGNITE-17316 >>>>>>>>>>> https://github.com/apache/ignite/pull/10140/files >>>>>>>>>>> >>>>>>>>>>> >>>>>>>>>>> On Fri, 1 Jul 2022 at 14:41, Pavel Tupitsyn < >>>>> ptupit...@apache.org> >>>>>>>>> wrote: >>>>>>>>>>>> >>>>>>>>>>>> I have no objections to extending the Thin Client >>>>> configuration with >>>>>>>>> a >>>>>>>>>>>> pluggable Affinity Function. >>>>>>>>>>>> Let's make it a normal Java setter though, system >>> properties >>>>> are >>>>>>>>> hacky. >>>>>>>>>>>> Especially when only some of the caches use custom >>> affinity, >>>>> as Maxim >>>>>>>>>>>> mentioned. >>>>>>>>>>>> >>>>>>>>>>>> On Wed, Jun 29, 2022 at 7:20 PM Николай Ижиков < >>>>> nizhi...@apache.org> >>>>>>>>>>> wrote: >>>>>>>>>>>> >>>>>>>>>>>>> +1 to have ability to specify custom affinity for PA >>> on >>>>> thin >>>>>>>>> client. >>>>>>>>>>>>> >>>>>>>>>>>>> It seems to me custom affinity is a rare use-case of >>>>> Ignite API. >>>>>>>>>>>>> Propose to have SystemProperty that can specify >>> affinity >>>>>>>>> implementation >>>>>>>>>>>>> for a thin client. >>>>>>>>>>>>> >>>>>>>>>>>>> >>>>>>>>>>>>>> 29 июня 2022 г., в 18:53, Maxim Muzafarov < >>>>> mmu...@apache.org> >>>>>>>>>>>>> написал(а): >>>>>>>>>>>>>> >>>>>>>>>>>>>> Igniters, >>>>>>>>>>>>>> >>>>>>>>>>>>>> >>>>>>>>>>>>>> I've faced with a customer's cluster which has more >>> than >>>>> 150 >>>>>>>>> nodes >>>>>>>>>>> and >>>>>>>>>>>>>> most for them are the thick-client nodes. Due to >>> each >>>>>>>>> thick-client is >>>>>>>>>>>>>> a full-fledged cluster topology participant it >>> affects >>>>> the >>>>>>>>> cluster >>>>>>>>>>>>>> discovery machinery during the system operation and >>>>> adding an >>>>>>>>>>>>>> additional overhead for using/deploying a new nodes >>> in >>>>> Kubernetes >>>>>>>>>>>>>> environment. However, the main thing from my point >>> of >>>>> view it >>>>>>>>>>> prevents >>>>>>>>>>>>>> updating the client side and server side components >>>>> independently >>>>>>>>>>>>>> (Apache Ignite doesn't support rolling upgrade). >>>>>>>>>>>>>> >>>>>>>>>>>>>> Accordingly to the assumptions above using thin >>> clients >>>>> become a >>>>>>>>>>>>>> necessary. This looks even more attractive, since >>> the >>>>> thin >>>>>>>>> client has >>>>>>>>>>>>>> a fairly rich API over the past few releases. >>>>>>>>>>>>>> >>>>>>>>>>>>>> >>>>>>>>>>>>>> The MAIN ISSUE here that blocks thin client usage is >>>>> that for >>>>>>>>> some of >>>>>>>>>>>>>> cache groups a custom affinity function (and an >>>>>>>>> AffinityKeyMapper) >>>>>>>>>>> was >>>>>>>>>>>>>> used which prevents enabling the Partition Awareness >>>>> thin client >>>>>>>>>>>>>> feature. Thus each cache request will have two hops. >>>>>>>>>>>>>> >>>>>>>>>>>>>> Of course, we can force users to migrate to a new >>> API, >>>>> but this >>>>>>>>>>>>>> becomes more difficult when Apache Ignite is part >>> of a >>>>> much >>>>>>>>> larger >>>>>>>>>>>>>> architectural solution and thus it is doent' looks >>> so >>>>> friendly. >>>>>>>>>>>>>> >>>>>>>>>>>>>> The MAIN QUESTION here - does anyone know our users >>> who >>>>> have >>>>>>>>>>>>>> encountered with the same issue? I want to solve >>> such a >>>>> problem >>>>>>>>> once >>>>>>>>>>>>>> and make all such users happy by implementing the >>> general >>>>>>>>> approach. >>>>>>>>>>>>>> >>>>>>>>>>>>>> >>>>>>>>>>>>>> = Possible solutions = >>>>>>>>>>>>>> >>>>>>>>>>>>>> >>>>>>>>>>>>>> 1. Making an affinity function pluggable (mapping >>>>> calculations) >>>>>>>>> on >>>>>>>>>>> the >>>>>>>>>>>>>> thin clients side. Currently the >>>>> RendezvousAffinityFunction [1] >>>>>>>>> is >>>>>>>>>>>>>> only supported >>>>>>>>>>>>>> for the partition awareness. A user's affinity >>> function >>>>> seems to >>>>>>>>> be >>>>>>>>>>>>>> the stateless function due to there is no machinery >>> to >>>>> transfer >>>>>>>>>>> states >>>>>>>>>>>>>> to the thin client. >>>>>>>>>>>>>> >>>>>>>>>>>>>> Pros - a general solution for all such cases; >>>>>>>>>>>>>> Cons - unnecessary complexity, extending public API; >>>>>>>>>>>>>> >>>>>>>>>>>>>> >>>>>>>>>>>>>> 2. Creating an Ignite extension which will extend >>> the >>>>> thin >>>>>>>>> client API >>>>>>>>>>>>>> thus a user will have a full control over a >>> destination >>>>> node to >>>>>>>>> which >>>>>>>>>>>>>> requests being sent. >>>>>>>>>>>>>> >>>>>>>>>>>>>> Pros - isolated solution, simple implementation; >>>>>>>>>>>>>> Cons - hard to support spring-boot-thin-client etc. >>> and >>>>> other >>>>>>>>>>>>>> extensions based on the thin client API; >>>>>>>>>>>>>> >>>>>>>>>>>>>> >>>>>>>>>>>>>> Folks, please share your thoughts. >>>>>>>>>>>>>> >>>>>>>>>>>>>> >>>>>>>>>>>>>> [1] >>>>>>>>>>>>> >>>>>>>>>>> >>>>>>>>> >>>>> >>> https://github.com/apache/ignite/blob/master/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCachePartitionsRequest.java#L206 >>>>>>>>>>>>> >>>>>>>>>>>>> >>>>>>>>>>> >>>>>>>>> >>>>> >>> >>