Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
"iterations" : 4,
"threads" : 1,
"forks" : 3,
"mean_ops" : 823635.7718335792
"mean_ops" : 806565.5933954978
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
"iterations" : 4,
"threads" : 1,
"forks" : 3,
"mean_ops" : 655010.8023043653
"mean_ops" : 618403.982977282
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import io.appform.ranger.discovery.bundle.rotationstatus.RotationStatus;
import io.appform.ranger.discovery.bundle.selectors.HierarchicalEnvironmentAwareShardSelector;
import io.appform.ranger.discovery.bundle.util.ConfigurationUtils;
import io.appform.ranger.discovery.bundle.util.NodeUtils;
import io.appform.ranger.zookeeper.ServiceProviderBuilders;
import io.appform.ranger.zookeeper.serde.ZkNodeDataSerializer;
import io.dropwizard.Configuration;
Expand Down Expand Up @@ -317,7 +318,8 @@ public void start() {
serviceProvider.start();
serviceDiscoveryClient.start();
val nodeIdManager = new NodeIdManager(curator, serviceName);
IdGenerator.initialize(nodeIdManager.fixNodeId(), globalIdConstraints, Collections.emptyMap());
NodeUtils.setNode(nodeIdManager.fixNodeId());
IdGenerator.initialize(globalIdConstraints, Collections.emptyMap());
log.debug("Discovery manager has been successfully started.");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
@ToString
public class Id {
private String id;
private String prefix;
private String suffix;
private Date generatedDate;
private int node;
private int exponent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import io.appform.ranger.discovery.bundle.id.generator.DefaultIdGenerator;
import io.appform.ranger.discovery.bundle.id.generator.IdGeneratorBase;
import io.appform.ranger.discovery.bundle.id.request.IdGenerationRequest;
import io.dropwizard.logback.shaded.guava.annotations.VisibleForTesting;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;

Expand All @@ -37,6 +38,7 @@
public class IdGenerator {
private static final IdGeneratorBase baseGenerator = new DefaultIdGenerator();

@VisibleForTesting
public static void initialize(int node) {
baseGenerator.setNodeId(node);
}
Expand All @@ -46,9 +48,8 @@ public static synchronized void cleanUp() {
}

public static synchronized void initialize(
int node, List<IdValidationConstraint> globalConstraints,
List<IdValidationConstraint> globalConstraints,
Map<String, List<IdValidationConstraint>> domainSpecificConstraints) {
initialize(node);
if(null != globalConstraints && !globalConstraints.isEmpty() ) {
baseGenerator.registerGlobalConstraints(globalConstraints);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class IdFormatters {

private static final IdFormatter originalIdFormatter = new DefaultIdFormatter();
private static final IdFormatter base36IdFormatter = new Base36IdFormatter(originalIdFormatter);
private static final IdFormatter suffixIdFormatter = new SuffixIdFormatter();

public static IdFormatter original() {
return originalIdFormatter;
Expand All @@ -31,4 +32,7 @@ public static IdFormatter base36() {
return base36IdFormatter;
}

public static IdFormatter suffix() {
return suffixIdFormatter;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

@Getter
public enum IdParserType {
DEFAULT (0);
DEFAULT (00),
SUFFIX (01),
BASE36_SUFFIX(02);

private final int value;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ public class IdParsers {
private static final Pattern PATTERN = Pattern.compile("([A-Za-z]*)([0-9]{22})([0-9]{2})?(.*)");

private final Map<Integer, IdFormatter> parserRegistry = Map.of(
IdFormatters.original().getType().getValue(), IdFormatters.original()
IdFormatters.original().getType().getValue(), IdFormatters.original(),
IdFormatters.suffix().getType().getValue(), IdFormatters.suffix()
);

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2024 Authors, Flipkart Internet Pvt. Ltd.
*
* Licensed 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 io.appform.ranger.discovery.bundle.id.formatter;

import io.appform.ranger.discovery.bundle.id.Id;
import lombok.val;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import java.util.Optional;
import java.util.regex.Pattern;

public class SuffixIdFormatter implements IdFormatter {
private static final Pattern PATTERN = Pattern.compile("([A-Za-z]*)([0-9]{15})([0-9]{4})([0-9]{3})([0-9]{2})([0-9]*)");
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormat.forPattern("yyMMddHHmmssSSS");

@Override
public IdParserType getType() {
return IdParserType.SUFFIX;
}

@Override
public String format(final DateTime dateTime,
final int nodeId,
final int randomNonce) {
return String.format("%s%04d%03d%02d", DATE_TIME_FORMATTER.print(dateTime), nodeId, randomNonce, getType().getValue());
}

@Override
public Optional<Id> parse(final String idString) {
val matcher = PATTERN.matcher(idString);
if (!matcher.find()) {
return Optional.empty();
}
return Optional.of(Id.builder()
.id(idString)
.prefix(matcher.group(1))
.suffix(matcher.group(6))
.node(Integer.parseInt(matcher.group(3)))
.exponent(Integer.parseInt(matcher.group(4)))
.generatedDate(DATE_TIME_FORMATTER.parseDateTime(matcher.group(2)).toDate())
.build());
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.appform.ranger.discovery.bundle.id.generator;

import io.appform.ranger.discovery.bundle.id.formatter.IdFormatter;
import io.appform.ranger.discovery.bundle.id.formatter.IdFormatters;
import io.appform.ranger.discovery.bundle.id.nonce.RandomNonceGenerator;

Expand All @@ -8,4 +9,8 @@ public class DefaultIdGenerator extends IdGeneratorBase {
public DefaultIdGenerator() {
super(IdFormatters.original(), new RandomNonceGenerator());
}

public DefaultIdGenerator(final IdFormatter idFormatter) {
super(idFormatter, new RandomNonceGenerator());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
import io.appform.ranger.discovery.bundle.id.IdValidationState;
import io.appform.ranger.discovery.bundle.id.constraints.IdValidationConstraint;
import io.appform.ranger.discovery.bundle.id.formatter.IdFormatter;
import io.appform.ranger.discovery.bundle.id.formatter.IdFormatters;
import io.appform.ranger.discovery.bundle.id.nonce.NonceGenerator;
import io.appform.ranger.discovery.bundle.id.request.IdGenerationInput;
import io.appform.ranger.discovery.bundle.id.request.IdGenerationRequest;
import io.appform.ranger.discovery.bundle.util.NodeUtils;
import lombok.Getter;
import lombok.val;
import org.joda.time.DateTime;
Expand Down Expand Up @@ -53,6 +53,7 @@ protected IdGeneratorBase(final IdFormatter idFormatter,
.handleResultIf(generationResult -> generationResult.getState() == IdValidationState.INVALID_RETRYABLE)
.onRetry(nonceGenerator::retryEventListener)
.build();
this.nodeId = NodeUtils.getNode();
this.retryer = Failsafe.with(Collections.singletonList(retryPolicy));
}

Expand All @@ -78,7 +79,7 @@ public final synchronized void registerDomainSpecificConstraints(
registeredDomains.computeIfAbsent(domain, key -> Domain.builder()
.domain(domain)
.constraints(validationConstraints)
.idFormatter(IdFormatters.original())
.idFormatter(idFormatter)
.resolution(TimeUnit.MILLISECONDS)
.build());
}
Expand All @@ -94,10 +95,25 @@ public final Id getIdFromIdInfo(final NonceInfo nonceInfo, final String namespac
.build();
}

public final Id getIdFromIdInfo(final NonceInfo nonceInfo, final String namespace, final String suffix, final IdFormatter idFormatter) {
val dateTime = new DateTime(nonceInfo.getTime());
val id = String.format("%s%s%s", namespace, idFormatter.format(dateTime, getNodeId(), nonceInfo.getExponent()), suffix != null ? suffix : "");
return Id.builder()
.id(id)
.exponent(nonceInfo.getExponent())
.generatedDate(dateTime.toDate())
.node(getNodeId())
.build();
}

public final Id getIdFromIdInfo(final NonceInfo nonceInfo, final String namespace) {
return getIdFromIdInfo(nonceInfo, namespace, idFormatter);
}

public final Id getIdFromIdInfo(final NonceInfo nonceInfo, final String namespace, final String suffix) {
return getIdFromIdInfo(nonceInfo, namespace, suffix, idFormatter);
}

public final IdValidationState validateId(final List<IdValidationConstraint> inConstraints, final Id id, final boolean skipGlobal) {
// First evaluate global constraints
val failedGlobalConstraint
Expand Down Expand Up @@ -139,6 +155,10 @@ public final Id generate(final String namespace) {
return getIdFromIdInfo(idInfo, namespace);
}

public final Id generate(final String namespace, final String suffix) {
val idInfo = nonceGenerator.generate(namespace);
return getIdFromIdInfo(idInfo, namespace, suffix);
}

public final Id generate(final String namespace, final IdFormatter idFormatter) {
val idInfo = nonceGenerator.generate(namespace);
Expand Down Expand Up @@ -167,23 +187,37 @@ public final Optional<Id> generateWithConstraints(final String namespace, final
return generateWithConstraints(request);
}

public Optional<Id> generateWithConstraints(
String namespace,
String suffix,
final List<IdValidationConstraint> inConstraints) {
return generateWithConstraints(IdGenerationRequest.builder()
.prefix(namespace)
.suffix(suffix)
.constraints(inConstraints)
.skipGlobal(false)
.idFormatter(idFormatter)
.build());
}

public final Optional<Id> generateWithConstraints(final IdGenerationRequest request) {
val domain = request.getDomain() != null ? registeredDomains.getOrDefault(request.getDomain(), Domain.DEFAULT) : Domain.DEFAULT;
val idGenerationInput = IdGenerationInput.builder()
.prefix(request.getPrefix())
.suffix(request.getSuffix())
.domain(domain)
.build();
return Optional.ofNullable(retryer.get(
() -> {
val idInfoOptional = nonceGenerator.generateWithConstraints(idGenerationInput);
val id = getIdFromIdInfo(idInfoOptional, request.getPrefix(), request.getIdFormatter());
val id = getIdFromIdInfo(idInfoOptional, request.getPrefix(), request.getSuffix(), request.getIdFormatter());
return new GenerationResult(
idInfoOptional,
validateId(request.getConstraints(), id, request.isSkipGlobal()),
domain);
}))
.filter(generationResult -> generationResult.getState() == IdValidationState.VALID)
.map(generationResult -> this.getIdFromIdInfo(generationResult.getNonceInfo(), request.getPrefix(), request.getIdFormatter()));
.map(generationResult -> this.getIdFromIdInfo(generationResult.getNonceInfo(), request.getPrefix(), request.getSuffix(), request.getIdFormatter()));
}

public final void setNodeId(int nodeId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
@Builder
public class IdGenerationInput {
String prefix;
String suffix;
Domain domain;

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
public class IdGenerationRequest {

String prefix;
String suffix;
String domain;
boolean skipGlobal;
List<IdValidationConstraint> constraints;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.appform.ranger.discovery.bundle.util;

import lombok.Getter;
import lombok.Setter;
import lombok.experimental.UtilityClass;

@UtilityClass
public class NodeUtils {

@Getter
@Setter
private int node;
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package io.appform.ranger.discovery.bundle.id;

import io.appform.ranger.discovery.bundle.id.formatter.IdFormatters;
import io.appform.ranger.discovery.bundle.id.formatter.IdParsers;
import io.appform.ranger.discovery.bundle.id.generator.DefaultIdGenerator;
import lombok.val;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.Optional;

public class IdParsersTest {

Expand All @@ -22,6 +26,45 @@ void testDefaultId() throws ParseException {
assertDate("240710123233616", parsedId.getGeneratedDate());
}

@Test
void testParseSuccessAfterGenerationWithSuffix() {
val idGenerator = new DefaultIdGenerator(IdFormatters.suffix());
val prefix = "TEST";
val suffix = "007";
val generatedId = idGenerator.generate(prefix, suffix);
val parsedId = IdGenerator.parse(generatedId.getId()).orElse(null);
Assertions.assertNotNull(parsedId);
Assertions.assertEquals(prefix, parsedId.getPrefix());
Assertions.assertEquals(suffix, parsedId.getSuffix());
Assertions.assertEquals(parsedId.getId(), generatedId.getId());
Assertions.assertEquals(parsedId.getExponent(), generatedId.getExponent());
Assertions.assertEquals(parsedId.getNode(), generatedId.getNode());
Assertions.assertEquals(parsedId.getGeneratedDate(), generatedId.getGeneratedDate());
}

@Test
void testParseSuccessAfterGenerationWithConstraintsSuffix() {
val idGenerator = new DefaultIdGenerator(IdFormatters.suffix());
val prefix = "TEST";
val suffix = "007";
val domain = "TEST";

idGenerator.registerDomainSpecificConstraints(domain, Collections.singletonList(id -> true));
Optional<Id> id = idGenerator.generateWithConstraints(prefix, suffix, Collections.emptyList());

Assertions.assertTrue(id.isPresent());
Assertions.assertEquals(31, id.get().getId().length());

val parsedId = IdGenerator.parse(id.get().getId()).orElse(null);
Assertions.assertNotNull(parsedId);
Assertions.assertEquals(prefix, parsedId.getPrefix());
Assertions.assertEquals(suffix, parsedId.getSuffix());
Assertions.assertEquals(parsedId.getId(), id.get().getId());
Assertions.assertEquals(parsedId.getExponent(), id.get().getExponent());
Assertions.assertEquals(parsedId.getNode(), id.get().getNode());
Assertions.assertEquals(parsedId.getGeneratedDate(), id.get().getGeneratedDate());
}

private void assertDate(final String dateString, final Date date) throws ParseException {
Assertions.assertEquals(new SimpleDateFormat("yyMMddHHmmssSSS").parse(dateString), date);
}
Expand Down