atomix / atomix

A Kubernetes toolkit for building distributed applications using cloud native principles
https://atomix.io
Apache License 2.0
2.35k stars 386 forks source link

multiple primitive groups will throw exception on getting primitives #839

Closed linehrr closed 6 years ago

linehrr commented 6 years ago

Expected behavior

when Atomix cluster is configured with multiple primitive groups, it should give option to choose which group user wants to create primitive on. same for getting the primitives, so user can specify not just the primitive name, but also the primitive group

Actual behavior

when multiple primitive groups are configured, getting primitives by name will throw exception:

Exception in thread "main" io.atomix.utils.config.ConfigurationException: Primitive protocol is ambiguous: 2 partition groups found (data, raft)
    at io.atomix.primitive.PrimitiveBuilder.protocol(PrimitiveBuilder.java:125)
    at io.atomix.primitive.PrimitiveBuilder.newProxy(PrimitiveBuilder.java:163)
    at io.atomix.core.queue.impl.DefaultDistributedQueueBuilder.buildAsync(DefaultDistributedQueueBuilder.java:42)
    at io.atomix.core.impl.CorePrimitivesService.lambda$getPrimitiveAsync$0(CorePrimitivesService.java:341)
    at io.atomix.core.impl.CorePrimitiveCache.lambda$getPrimitive$0(CorePrimitiveCache.java:35)
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)
    at io.atomix.core.impl.CorePrimitiveCache.getPrimitive(CorePrimitiveCache.java:35)
    at io.atomix.core.impl.CorePrimitivesService.getPrimitiveAsync(CorePrimitivesService.java:336)
    at io.atomix.core.impl.CorePrimitivesService.getPrimitiveAsync(CorePrimitivesService.java:329)
    at io.atomix.primitive.PrimitiveFactory.getPrimitive(PrimitiveFactory.java:91)
    at io.atomix.core.impl.CorePrimitivesService.getQueue(CorePrimitivesService.java:237)
    at io.atomix.core.Atomix.getQueue(Atomix.java:594)

Steps to reproduce

  1. setup Atomix cluster

    val atomix = Atomix.builder()
      .withMemberId(memberId)
      .withAddress(address)
      .withMembershipProvider(
        BootstrapDiscoveryProvider.builder()
          .withNodes(
            Node.builder()
              .withId("member-1")
              .withAddress("localhost:5671")
              .build(),
            Node.builder()
              .withId("member-2")
              .withAddress("localhost:5672")
              .build(),
            Node.builder()
              .withId("member-3")
              .withAddress("localhost:5673")
              .build()
          )
          .withFailureThreshold(2)
          .build()
      )
      .withManagementGroup(
        PrimaryBackupPartitionGroup.builder("system")
          .withNumPartitions(32)
          .build()
      )
      .withPartitionGroups(
        PrimaryBackupPartitionGroup.builder("data")   // first primitive group using PB partition
          .withNumPartitions(32)
          .build(),
        RaftPartitionGroup.builder("raft")                       // second primitive group using Raft partition
          .withDataDirectory(new File(s"/tmp/$memberId"))
          .withNumPartitions(6)
          .withMembers("member-1", "member-2", "member-3")
          .build()
      )
      .build()
  2. create a queue with some specific protocol

    val protocol = MultiPrimaryProtocol.builder()
    .withBackups(2)
    .withConsistency(Consistency.LINEARIZABLE)
    .withReplication(Replication.SYNCHRONOUS)
    .build()
    
    val queue = atomix.queueBuilder[String]("my-queue")
    .withProtocol(protocol)
    .build
  3. get queue primitive from another node afterwards

    val queue = atomix.
    getQueue[String]("my-queue")

Environment

linehrr commented 6 years ago

discard, protocol builder has an option, but it's just not very clear in the document.

  val protocol = MultiRaftProtocol.builder("raft")                // here you can specify the group name
    .withReadConsistency(ReadConsistency.LINEARIZABLE)
    .build()