Skip to content

Commit 7688a55

Browse files
authored
NoSQL: Realms handling (#3007)
Introduces handling for realms including realm-state management/transition. The `RealmStore` implementation for NoSQL depends on CDI components, coming in a follo-up PR.
1 parent 7c8b7a2 commit 7688a55

File tree

18 files changed

+1084
-0
lines changed

18 files changed

+1084
-0
lines changed

bom/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ dependencies {
4848
api(project(":polaris-nodes-impl"))
4949
api(project(":polaris-nodes-spi"))
5050

51+
api(project(":polaris-persistence-nosql-realms-api"))
52+
api(project(":polaris-persistence-nosql-realms-impl"))
53+
api(project(":polaris-persistence-nosql-realms-spi"))
54+
5155
api(project(":polaris-persistence-nosql-api"))
5256
api(project(":polaris-persistence-nosql-impl"))
5357
api(project(":polaris-persistence-nosql-benchmark"))

gradle/projects.main.properties

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ polaris-idgen-spi=persistence/nosql/idgen/spi
6262
polaris-nodes-api=persistence/nosql/nodes/api
6363
polaris-nodes-impl=persistence/nosql/nodes/impl
6464
polaris-nodes-spi=persistence/nosql/nodes/spi
65+
# realms
66+
polaris-persistence-nosql-realms-api=persistence/nosql/realms/api
67+
polaris-persistence-nosql-realms-impl=persistence/nosql/realms/impl
68+
polaris-persistence-nosql-realms-spi=persistence/nosql/realms/spi
6569
# persistence / database agnostic
6670
polaris-persistence-nosql-api=persistence/nosql/persistence/api
6771
polaris-persistence-nosql-impl=persistence/nosql/persistence/impl

persistence/nosql/realms/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<!--
2+
Licensed to the Apache Software Foundation (ASF) under one
3+
or more contributor license agreements. See the NOTICE file
4+
distributed with this work for additional information
5+
regarding copyright ownership. The ASF licenses this file
6+
to you under the Apache License, Version 2.0 (the
7+
"License"); you may not use this file except in compliance
8+
with the License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing,
13+
software distributed under the License is distributed on an
14+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
KIND, either express or implied. See the License for the
16+
specific language governing permissions and limitations
17+
under the License.
18+
-->
19+
20+
# Dynamic realm management
21+
22+
Framework to manage realms.
23+
24+
## Code structure
25+
26+
The code is structured into multiple modules. Consuming code should almost always pull in only the API module.
27+
28+
* `polaris-persistence-nosql-realms-api` provides the necessary Java interfaces and immutable types.
29+
* `polaris-persistence-nosql-realms-id` provides a type-safe holder for a realm ID.
30+
* `polaris-persistence-nosql-realms-impl` provides the storage agnostic implementation.
31+
* `polaris-persistence-nosql-realms-spi` provides the necessary interfaces to provide a storage specific implementation.
32+
* `polaris-persistence-nosql-realms-store-nosql` provides the storage implementation based on `polaris-persistence-nosql-api`.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
plugins {
21+
id("org.kordamp.gradle.jandex")
22+
id("polaris-server")
23+
}
24+
25+
description = "Polaris realms API, no concrete implementations"
26+
27+
dependencies {
28+
implementation(project(":polaris-idgen-api"))
29+
implementation(libs.guava)
30+
31+
compileOnly(project(":polaris-immutables"))
32+
annotationProcessor(project(":polaris-immutables", configuration = "processor"))
33+
34+
implementation(platform(libs.jackson.bom))
35+
implementation("com.fasterxml.jackson.core:jackson-databind")
36+
37+
compileOnly(libs.jakarta.annotation.api)
38+
compileOnly(libs.jakarta.validation.api)
39+
compileOnly(libs.jakarta.inject.api)
40+
compileOnly(libs.jakarta.enterprise.cdi.api)
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.polaris.persistence.nosql.realms.api;
20+
21+
public class RealmAlreadyExistsException extends RuntimeException {
22+
public RealmAlreadyExistsException(String message) {
23+
super(message);
24+
}
25+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.polaris.persistence.nosql.realms.api;
20+
21+
import com.fasterxml.jackson.annotation.JsonIgnore;
22+
import com.fasterxml.jackson.annotation.JsonInclude;
23+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
24+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
25+
import java.time.Instant;
26+
import java.util.Map;
27+
import org.apache.polaris.immutables.PolarisImmutable;
28+
import org.immutables.value.Value;
29+
30+
@PolarisImmutable
31+
@JsonSerialize(as = ImmutableRealmDefinition.class)
32+
@JsonDeserialize(as = ImmutableRealmDefinition.class)
33+
public interface RealmDefinition {
34+
String id();
35+
36+
Instant created();
37+
38+
Instant updated();
39+
40+
RealmStatus status();
41+
42+
@JsonInclude(JsonInclude.Include.NON_EMPTY)
43+
Map<String, String> properties();
44+
45+
static ImmutableRealmDefinition.Builder builder() {
46+
return ImmutableRealmDefinition.builder();
47+
}
48+
49+
@JsonIgnore
50+
@Value.NonAttribute
51+
default boolean needsBootstrap() {
52+
return switch (status()) {
53+
case CREATED, LOADING, INITIALIZING -> true;
54+
default -> false;
55+
};
56+
}
57+
58+
/** Realms are assigned */
59+
enum RealmStatus {
60+
/**
61+
* The initial state of a realm is "created", which means that the realm ID is reserved, but the
62+
* realm is not yet usable. This state can transition to {@link #LOADING} or {@link
63+
* #INITIALIZING} or the realm can be directly deleted.
64+
*/
65+
CREATED,
66+
/**
67+
* State used to indicate that the realm data is being imported. This state can transition to
68+
* {@link #ACTIVE} or {@link #INACTIVE} or {@link #PURGING}.
69+
*/
70+
LOADING,
71+
/**
72+
* State used to indicate that the realm is being initialized. This state can transition to
73+
* {@link #ACTIVE} or {@link #INACTIVE} or {@link #PURGING}.
74+
*/
75+
INITIALIZING,
76+
/**
77+
* When a realm is fully set up, its state is "active". This state can only transition to {@link
78+
* #INACTIVE}.
79+
*/
80+
ACTIVE,
81+
/**
82+
* An {@link #ACTIVE} realm can be put into "inactive" state, which means that the realm cannot
83+
* be used, but it can be put back into {@link #ACTIVE} state.
84+
*/
85+
INACTIVE,
86+
/**
87+
* An {@link #INACTIVE} realm can be put into "purging" state, which means that the realm's data
88+
* is being purged from the persistence database. This is next to the final and terminal state
89+
* {@link #PURGED} of a realm. Once all data of the realm has been purged, it must at least be
90+
* set into {@link #PURGED} status or be entirely removed.
91+
*/
92+
PURGING,
93+
/**
94+
* "Purged" is the terminal state of every realm. A purged realm can be safely {@linkplain
95+
* RealmManagement#delete(RealmDefinition) deleted}. The difference between a "purged" realm and
96+
* a non-existing (deleted) realm is that the ID of a purged realm cannot be (re)used.
97+
*/
98+
PURGED,
99+
}
100+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.polaris.persistence.nosql.realms.api;
20+
21+
public class RealmExpectedStateMismatchException extends RuntimeException {
22+
public RealmExpectedStateMismatchException(String message) {
23+
super(message);
24+
}
25+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.polaris.persistence.nosql.realms.api;
20+
21+
import com.google.errorprone.annotations.MustBeClosed;
22+
import jakarta.annotation.Nonnull;
23+
import java.util.Optional;
24+
import java.util.stream.Stream;
25+
26+
/**
27+
* Low-level realm management functionality.
28+
*
29+
* <p>Realm IDs must conform to the following constraints:
30+
*
31+
* <ul>
32+
* <li>Must not start or end with whitespaces.
33+
* <li>Must only consist of US-ASCII letters or digits or hyphens ({@code -}) or underscores
34+
* ({@code _}).
35+
* <li>Must not start with two consecutive colons ({@code ::}).
36+
* <li>Must not be empty.
37+
* <li>Must not be longer than 128 characters.
38+
* </ul>
39+
*
40+
* <p>Note: In a CDI container {@link RealmManagement} can be directly injected.
41+
*/
42+
public interface RealmManagement {
43+
/**
44+
* Creates a new realm in {@linkplain RealmDefinition.RealmStatus#CREATED created status} with the
45+
* given realm ID.
46+
*
47+
* @return the persisted state of the realm definition
48+
* @throws RealmAlreadyExistsException if a realm with the given ID already exists
49+
*/
50+
@Nonnull
51+
RealmDefinition create(@Nonnull String realmId);
52+
53+
/** Returns a stream of all realm definitions. The returned stream must be closed. */
54+
@Nonnull
55+
@MustBeClosed
56+
Stream<RealmDefinition> list();
57+
58+
/**
59+
* Retrieve a realm definition by realm ID.
60+
*
61+
* @return the realm definition if it exists.
62+
*/
63+
@Nonnull
64+
Optional<RealmDefinition> get(@Nonnull String realmId);
65+
66+
/**
67+
* Updates a realm definition to {@code update}, if the persisted state matches the {@code
68+
* expected} state, and if the {@linkplain RealmDefinition#status() status} transition is valid.
69+
*
70+
* @param expected The expected persisted state of the realm definition. This must exactly
71+
* represent the persisted realm definition as returned by {@link #create(String)} or {@link
72+
* #get(String)} or a prior {@link #update(RealmDefinition, RealmDefinition)}.
73+
* @param update the new state of the realm definition to be persisted, the {@link
74+
* RealmDefinition#created() created} and {@link RealmDefinition#updated() updated} attributes
75+
* are solely managed by the implementation.
76+
* @return the persisted state of the realm definition
77+
* @throws RealmNotFoundException if a realm with the given ID does not exist
78+
* @throws RealmExpectedStateMismatchException if the expected state does not match
79+
* @throws IllegalArgumentException if the transition is not valid.
80+
*/
81+
@Nonnull
82+
RealmDefinition update(@Nonnull RealmDefinition expected, @Nonnull RealmDefinition update);
83+
84+
/**
85+
* Deletes the given realm.
86+
*
87+
* @param expected The expected persisted state of the realm definition. This must exactly
88+
* represent the persisted realm definition as returned by {@link #create(String)} or {@link
89+
* #get(String)} or {@link #update(RealmDefinition, RealmDefinition)}.
90+
* @throws RealmNotFoundException if a realm with the given ID does not exist
91+
* @throws RealmExpectedStateMismatchException if the expected state does not match
92+
*/
93+
void delete(@Nonnull RealmDefinition expected);
94+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.polaris.persistence.nosql.realms.api;
20+
21+
public class RealmNotFoundException extends RuntimeException {
22+
public RealmNotFoundException(String message) {
23+
super(message);
24+
}
25+
}

0 commit comments

Comments
 (0)