- 
                Notifications
    You must be signed in to change notification settings 
- Fork 326
NoSQL: Node IDs - API, SPI + general implementation #2728
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| <!-- | ||
| Licensed to the Apache Software Foundation (ASF) under one | ||
| or more contributor license agreements. See the NOTICE file | ||
| distributed with this work for additional information | ||
| regarding copyright ownership. The ASF licenses this file | ||
| to you under the Apache License, Version 2.0 (the | ||
| "License"); you may not use this file except in compliance | ||
| with the License. You may obtain a copy of the License at | ||
|  | ||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|  | ||
| Unless required by applicable law or agreed to in writing, | ||
| software distributed under the License is distributed on an | ||
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| KIND, either express or implied. See the License for the | ||
| specific language governing permissions and limitations | ||
| under the License. | ||
| --> | ||
|  | ||
| # Uniquely identify running Polaris nodes | ||
|  | ||
| Some ID generation mechanisms, | ||
| like [Snowflake-IDs](https://medium.com/@jitenderkmr/demystifying-snowflake-ids-a-unique-identifier-in-distributed-computing-72796a827c9d), | ||
| require unique integer IDs for each running node. This framework provides a mechanism to assign each running node a | ||
| unique integer ID. | ||
|  | ||
| ## Code structure | ||
|  | ||
| The code is structured into multiple modules. Consuming code should almost always pull in only the API module. | ||
|  | ||
| * `polaris-nodes-api` provides the necessary Java interfaces and immutable types. | ||
| * `polaris-nodes-impl` provides the storage agnostic implementation. | ||
| * `polaris-nodes-spi` provides the necessary interfaces to provide a storage specific implementation. | ||
| 
      Comment on lines
    
      +31
     to 
      +33
    
   There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These modules are used by snowflake id generator only, can we merge it into the modules holding snowflake id generators? So that the snowflake id generator is more consistent and self-contained. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is a valid point 👍 I made a specific renaming proposal in the thread about the package name (above). | ||
| * `polaris-nodes-store-nosql` provides the storage implementation based on `polaris-persistence-nosql-api`. | ||
| 
      Comment on lines
    
      +31
     to 
      +34
    
   There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where is the module? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently it's in the end-to-end NoSQL PR: #1189 ... to be made available for review later (to allow for smaller, easier-to-review PRs, as discussed) | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one | ||
| * or more contributor license agreements. See the NOTICE file | ||
| * distributed with this work for additional information | ||
| * regarding copyright ownership. The ASF licenses this file | ||
| * to you under the Apache License, Version 2.0 (the | ||
| * "License"); you may not use this file except in compliance | ||
| * with the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, | ||
| * software distributed under the License is distributed on an | ||
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| * KIND, either express or implied. See the License for the | ||
| * specific language governing permissions and limitations | ||
| * under the License. | ||
| */ | ||
|  | ||
| plugins { | ||
| id("org.kordamp.gradle.jandex") | ||
| id("polaris-server") | ||
| } | ||
|  | ||
| description = "Polaris nodes API, no concrete implementations" | ||
|  | ||
| dependencies { | ||
| implementation(project(":polaris-idgen-api")) | ||
|  | ||
| compileOnly(project(":polaris-immutables")) | ||
| annotationProcessor(project(":polaris-immutables", configuration = "processor")) | ||
|  | ||
| implementation(platform(libs.jackson.bom)) | ||
| implementation("com.fasterxml.jackson.core:jackson-databind") | ||
|  | ||
| compileOnly(libs.jakarta.annotation.api) | ||
| compileOnly(libs.jakarta.validation.api) | ||
| compileOnly(libs.jakarta.inject.api) | ||
| compileOnly(libs.jakarta.enterprise.cdi.api) | ||
|  | ||
| compileOnly(libs.smallrye.config.core) | ||
| compileOnly(platform(libs.quarkus.bom)) | ||
| compileOnly("io.quarkus:quarkus-core") | ||
| } | 
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one | ||
| * or more contributor license agreements. See the NOTICE file | ||
| * distributed with this work for additional information | ||
| * regarding copyright ownership. The ASF licenses this file | ||
| * to you under the Apache License, Version 2.0 (the | ||
| * "License"); you may not use this file except in compliance | ||
| * with the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, | ||
| * software distributed under the License is distributed on an | ||
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| * KIND, either express or implied. See the License for the | ||
| * specific language governing permissions and limitations | ||
| * under the License. | ||
| */ | ||
| package org.apache.polaris.nodeids.api; | ||
|  | ||
| import jakarta.annotation.Nullable; | ||
| import java.time.Instant; | ||
| import org.apache.polaris.immutables.PolarisImmutable; | ||
|  | ||
| /** Represents the local node's ID and informative, mutable state information. */ | ||
| @PolarisImmutable | ||
| public interface Node { | ||
| /** | ||
| * Returns the ID of this node. | ||
| * | ||
| * @return ID of this node | ||
| * @throws IllegalStateException if the lease is no longer valid, for example, expired before it | ||
| * being renewed | ||
| */ | ||
| int id(); | ||
|  | ||
| default boolean valid(long nowInMillis) { | ||
| return nowInMillis < expirationTimestamp().toEpochMilli(); | ||
| } | ||
|  | ||
| Instant leaseTimestamp(); | ||
|  | ||
| @Nullable | ||
| Instant renewLeaseTimestamp(); | ||
|  | ||
| /** Timestamp since which this node's lease is no longer valid. */ | ||
| Instant expirationTimestamp(); | ||
| } | 
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one | ||
| * or more contributor license agreements. See the NOTICE file | ||
| * distributed with this work for additional information | ||
| * regarding copyright ownership. The ASF licenses this file | ||
| * to you under the Apache License, Version 2.0 (the | ||
| * "License"); you may not use this file except in compliance | ||
| * with the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, | ||
| * software distributed under the License is distributed on an | ||
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| * KIND, either express or implied. See the License for the | ||
| * specific language governing permissions and limitations | ||
| * under the License. | ||
| */ | ||
| package org.apache.polaris.nodeids.api; | ||
|  | ||
| import jakarta.annotation.Nullable; | ||
|  | ||
| public interface NodeLease { | ||
| /** | ||
| * Returns the {@link Node} representation for this lease if the lease has not been released or | ||
| * {@code null}. | ||
| */ | ||
| @Nullable | ||
| Node node(); | ||
|  | ||
| /** | ||
| * Permanently release the lease. Does nothing, if already released. Throws if persisting the | ||
| * released state fails. | ||
| */ | ||
| void release(); | ||
|  | ||
| /** | ||
| * Force a lease renewal, generally not recommended nor necessary. Throws, if the lease is already | ||
| * released. | ||
| */ | ||
| void renew(); | ||
|  | ||
| /** Returns the node ID if the lease is active/valid or {@code -1}. */ | ||
| int nodeIdIfValid(); | ||
| } | 
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one | ||
| * or more contributor license agreements. See the NOTICE file | ||
| * distributed with this work for additional information | ||
| * regarding copyright ownership. The ASF licenses this file | ||
| * to you under the Apache License, Version 2.0 (the | ||
| * "License"); you may not use this file except in compliance | ||
| * with the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, | ||
| * software distributed under the License is distributed on an | ||
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| * KIND, either express or implied. See the License for the | ||
| * specific language governing permissions and limitations | ||
| * under the License. | ||
| */ | ||
| package org.apache.polaris.nodeids.api; | ||
|  | ||
| import jakarta.annotation.Nonnull; | ||
| import jakarta.enterprise.context.ApplicationScoped; | ||
| import java.util.Optional; | ||
| import org.apache.polaris.ids.api.IdGenerator; | ||
| import org.apache.polaris.ids.api.SnowflakeIdGenerator; | ||
|  | ||
| /** | ||
| * API to lease node IDs, primarily to generate {@linkplain SnowflakeIdGenerator snowflake IDs}. | ||
| * | ||
| * <p>The default configuration for the snowflake IDs allows generation of 4096 IDs per millisecond | ||
| * (12 sequence bits), which should be more than enough. As a consequence, it is very likely enough | ||
| * to have only one ID generator per JVM, across all realms and catalogs. | ||
| * | ||
| * <p>Implementation is provided as an {@link ApplicationScoped @ApplicationScoped} bean | ||
| */ | ||
| public interface NodeManagement extends AutoCloseable { | ||
| /** | ||
| * Build a <em>new</em> and <em>independent</em> ID generator instance of a {@linkplain #lease() | ||
| * leased node} using the given clock. | ||
| * | ||
| * <p>This function must only be called from {@link ApplicationScoped @ApplicationScoped} CDI | ||
| * producers providing the same {@link IdGenerator} for the lifetime of the given {@link Node}, | ||
| * aka at most once for a {@link Node} instance. | ||
| */ | ||
| IdGenerator buildIdGenerator(@Nonnull NodeLease leasedNode); | ||
|  | ||
| /** The maximum number of concurrently leased nodes that are supported. */ | ||
| int maxNumberOfNodes(); | ||
|  | ||
| /** Retrieve information about a specific node. */ | ||
| Optional<Node> getNodeInfo(int nodeId); | ||
|  | ||
| /** Get the persistence ID for a node by its ID. */ | ||
| long systemIdForNode(int nodeId); | ||
|  | ||
| /** | ||
| * Lease a node. | ||
| * | ||
| * <p>The implementation takes care of periodically renewing the lease. It is not necessary to | ||
| * explicitly call {@link NodeLease#renew()}. | ||
| * | ||
| * <p>It is possible that the {@linkplain NodeLease#nodeIdIfValid() ID of the leased node} changes | ||
| * over time. | ||
| * | ||
| * <p>Each invocation returns a new, independent lease. | ||
| * | ||
| * @return the leased node | ||
| * @throws IllegalStateException if no node ID could be leased | ||
| */ | ||
| @Nonnull | ||
| NodeLease lease(); | ||
| } | 
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one | ||
| * or more contributor license agreements. See the NOTICE file | ||
| * distributed with this work for additional information | ||
| * regarding copyright ownership. The ASF licenses this file | ||
| * to you under the Apache License, Version 2.0 (the | ||
| * "License"); you may not use this file except in compliance | ||
| * with the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, | ||
| * software distributed under the License is distributed on an | ||
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| * KIND, either express or implied. See the License for the | ||
| * specific language governing permissions and limitations | ||
| * under the License. | ||
| */ | ||
| package org.apache.polaris.nodeids.api; | ||
|  | ||
| import com.fasterxml.jackson.databind.annotation.JsonDeserialize; | ||
| import com.fasterxml.jackson.databind.annotation.JsonSerialize; | ||
| import io.smallrye.config.ConfigMapping; | ||
| import io.smallrye.config.WithDefault; | ||
| import java.time.Duration; | ||
| import org.apache.polaris.ids.api.IdGenerator; | ||
| import org.apache.polaris.ids.api.IdGeneratorSpec; | ||
| import org.apache.polaris.ids.api.SnowflakeIdGenerator; | ||
| import org.apache.polaris.immutables.PolarisImmutable; | ||
| import org.immutables.value.Value; | ||
|  | ||
| /** Node management configuration. */ | ||
| @ConfigMapping(prefix = "polaris.node") | ||
| @JsonSerialize(as = ImmutableBuildableNodeManagementConfig.class) | ||
| @JsonDeserialize(as = ImmutableBuildableNodeManagementConfig.class) | ||
| public interface NodeManagementConfig { | ||
| /** Duration of a node-lease. */ | ||
| @WithDefault(DEFAULT_LEASE_DURATION) | ||
| Duration leaseDuration(); | ||
|  | ||
| /** Time window before the end of a node lease when the lease will be renewed. */ | ||
| @WithDefault(DEFAULT_RENEWAL_PERIOD) | ||
| Duration renewalPeriod(); | ||
|  | ||
| /** | ||
| * Maximum number of concurrently active Polaris nodes. Do not change this value or the ID | ||
| * generator spec, it is a rather internal property. See ID generator spec below. | ||
| */ | ||
| @WithDefault(DEFAULT_NUM_NODES) | ||
| int numNodes(); | ||
|  | ||
| /** | ||
| * Configuration needed to build an {@linkplain IdGenerator ID generator}. This configuration | ||
| * cannot be changed after one Polaris node has been successfully started. This specification will | ||
| * be ignored if a persisted one exists, but a warning shall be logged if both are different. | ||
| */ | ||
| IdGeneratorSpec idGeneratorSpec(); | ||
|  | ||
| String DEFAULT_LEASE_DURATION = "PT1H"; | ||
| String DEFAULT_RENEWAL_PERIOD = "PT15M"; | ||
| String DEFAULT_NUM_NODES = "" + (1 << SnowflakeIdGenerator.DEFAULT_NODE_ID_BITS); | ||
|  | ||
| @PolarisImmutable | ||
| interface BuildableNodeManagementConfig extends NodeManagementConfig { | ||
|  | ||
| static ImmutableBuildableNodeManagementConfig.Builder builder() { | ||
| return ImmutableBuildableNodeManagementConfig.builder(); | ||
| } | ||
|  | ||
| @Override | ||
| @Value.Default | ||
| default Duration leaseDuration() { | ||
| return Duration.parse(DEFAULT_LEASE_DURATION); | ||
| } | ||
|  | ||
| @Override | ||
| @Value.Default | ||
| default Duration renewalPeriod() { | ||
| return Duration.parse(DEFAULT_RENEWAL_PERIOD); | ||
| } | ||
|  | ||
| @Override | ||
| default int numNodes() { | ||
| return 1 << SnowflakeIdGenerator.DEFAULT_NODE_ID_BITS; | ||
| } | ||
| } | ||
| } | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If snowflake id generator requires such complex node id generator, maybe we should consider other options. Would it possible to use other id generators? Since we are in the persistence module already, why cannot we use something like
ObjectIDin mongoDB, or Java UUID?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The snowflake id generator is already used by the NoSQL persistence impl., of which this PR is just a sub-component.