-
Notifications
You must be signed in to change notification settings - Fork 86
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
[controller] Fix mismatch between hybrid version partition count and real-time partition count #1338
base: main
Are you sure you want to change the base?
[controller] Fix mismatch between hybrid version partition count and real-time partition count #1338
Changes from all commits
2386418
4b4113e
edc8621
7bbe45b
516e78e
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,178 @@ | ||
package com.linkedin.venice.controllerapi; | ||
|
||
import com.linkedin.venice.meta.Version.PushType; | ||
import java.security.cert.X509Certificate; | ||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
|
||
public class RequestTopicForPushRequest { | ||
private final String clusterName; | ||
private final String storeName; | ||
private final PushType pushType; | ||
private final String pushJobId; | ||
|
||
private boolean sendStartOfPush = false; | ||
private boolean sorted = false; // an inefficient but safe default | ||
private boolean isWriteComputeEnabled = false; | ||
private long rewindTimeInSecondsOverride = -1L; | ||
private boolean deferVersionSwap = false; | ||
private String targetedRegions = null; | ||
private int repushSourceVersion = -1; | ||
private Set<String> partitioners = Collections.emptySet(); | ||
private String compressionDictionary = null; | ||
private X509Certificate certificateInRequest = null; | ||
private String sourceGridFabric = null; | ||
private String emergencySourceRegion = null; | ||
|
||
public RequestTopicForPushRequest(String clusterName, String storeName, PushType pushType, String pushJobId) { | ||
if (clusterName == null || clusterName.isEmpty()) { | ||
throw new IllegalArgumentException("clusterName is required"); | ||
} | ||
if (storeName == null || storeName.isEmpty()) { | ||
throw new IllegalArgumentException("storeName is required"); | ||
} | ||
if (pushType == null) { | ||
throw new IllegalArgumentException("pushType is required"); | ||
} | ||
|
||
if (pushJobId == null || pushJobId.isEmpty()) { | ||
throw new IllegalArgumentException("pushJobId is required"); | ||
} | ||
|
||
this.clusterName = clusterName; | ||
this.storeName = storeName; | ||
this.pushType = pushType; | ||
this.pushJobId = pushJobId; | ||
} | ||
|
||
public static PushType extractPushType(String pushTypeString) { | ||
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. In the PushType enum itself, we already have an valueOf (int), would it be better to add this method there to have all the valueOf in one place? And this method is simply catch an illegalArgumentException and throw a new illegalArgumentException with a more informative error message. Catching exception is expensive. So maybe inside the PushType enum, you can pre-build a static map of pushType string, if the inputString does not match any, you can throw an illegalArgumentException. Just my two-cents. |
||
try { | ||
return PushType.valueOf(pushTypeString); | ||
} catch (IllegalArgumentException e) { | ||
throw new IllegalArgumentException( | ||
pushTypeString + " is an invalid push type. Valid push types are: " + Arrays.toString(PushType.values())); | ||
} | ||
} | ||
|
||
public String getClusterName() { | ||
return clusterName; | ||
} | ||
|
||
public String getStoreName() { | ||
return storeName; | ||
} | ||
|
||
public PushType getPushType() { | ||
return pushType; | ||
} | ||
|
||
public String getPushJobId() { | ||
return pushJobId; | ||
} | ||
|
||
public boolean isSendStartOfPush() { | ||
return sendStartOfPush; | ||
} | ||
|
||
public boolean isSorted() { | ||
return sorted; | ||
} | ||
|
||
public boolean isWriteComputeEnabled() { | ||
return isWriteComputeEnabled; | ||
} | ||
|
||
public String getSourceGridFabric() { | ||
return sourceGridFabric; | ||
} | ||
|
||
public long getRewindTimeInSecondsOverride() { | ||
return rewindTimeInSecondsOverride; | ||
} | ||
|
||
public boolean isDeferVersionSwap() { | ||
return deferVersionSwap; | ||
} | ||
|
||
public String getTargetedRegions() { | ||
return targetedRegions; | ||
} | ||
|
||
public int getRepushSourceVersion() { | ||
return repushSourceVersion; | ||
} | ||
|
||
public Set<String> getPartitioners() { | ||
return partitioners; | ||
} | ||
|
||
public String getCompressionDictionary() { | ||
return compressionDictionary; | ||
} | ||
|
||
public X509Certificate getCertificateInRequest() { | ||
return certificateInRequest; | ||
} | ||
|
||
public String getEmergencySourceRegion() { | ||
return emergencySourceRegion; | ||
} | ||
|
||
public void setSendStartOfPush(boolean sendStartOfPush) { | ||
this.sendStartOfPush = sendStartOfPush; | ||
} | ||
|
||
public void setSorted(boolean sorted) { | ||
this.sorted = sorted; | ||
} | ||
|
||
public void setWriteComputeEnabled(boolean writeComputeEnabled) { | ||
isWriteComputeEnabled = writeComputeEnabled; | ||
} | ||
|
||
public void setSourceGridFabric(String sourceGridFabric) { | ||
this.sourceGridFabric = sourceGridFabric; | ||
} | ||
|
||
public void setRewindTimeInSecondsOverride(long rewindTimeInSecondsOverride) { | ||
this.rewindTimeInSecondsOverride = rewindTimeInSecondsOverride; | ||
} | ||
|
||
public void setDeferVersionSwap(boolean deferVersionSwap) { | ||
this.deferVersionSwap = deferVersionSwap; | ||
} | ||
|
||
public void setTargetedRegions(String targetedRegions) { | ||
this.targetedRegions = targetedRegions; | ||
} | ||
|
||
public void setRepushSourceVersion(int repushSourceVersion) { | ||
this.repushSourceVersion = repushSourceVersion; | ||
} | ||
|
||
public void setPartitioners(String commaSeparatedPartitioners) { | ||
if (commaSeparatedPartitioners == null || commaSeparatedPartitioners.isEmpty()) { | ||
return; | ||
} | ||
setPartitioners(new HashSet<>(Arrays.asList(commaSeparatedPartitioners.split(",")))); | ||
} | ||
|
||
public void setPartitioners(Set<String> partitioners) { | ||
this.partitioners = partitioners; | ||
} | ||
|
||
public void setCompressionDictionary(String compressionDictionary) { | ||
this.compressionDictionary = compressionDictionary; | ||
} | ||
|
||
public void setCertificateInRequest(X509Certificate certificateInRequest) { | ||
this.certificateInRequest = certificateInRequest; | ||
} | ||
|
||
public void setEmergencySourceRegion(String emergencySourceRegion) { | ||
this.emergencySourceRegion = emergencySourceRegion; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package com.linkedin.venice.controllerapi; | ||
|
||
import static com.linkedin.venice.meta.Version.PushType.BATCH; | ||
import static com.linkedin.venice.meta.Version.PushType.STREAM; | ||
import static org.testng.Assert.assertEquals; | ||
import static org.testng.Assert.assertTrue; | ||
|
||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.HashSet; | ||
import org.testng.Assert; | ||
import org.testng.annotations.BeforeMethod; | ||
import org.testng.annotations.Test; | ||
|
||
|
||
public class RequestTopicForPushRequestTest { | ||
private RequestTopicForPushRequest request; | ||
|
||
@BeforeMethod | ||
public void setUp() { | ||
request = new RequestTopicForPushRequest("clusterA", "storeA", BATCH, "job123"); | ||
} | ||
|
||
@Test | ||
public void testRequestTopicForPushRequestConstructorArgs() { | ||
assertEquals(request.getClusterName(), "clusterA"); | ||
assertEquals(request.getStoreName(), "storeA"); | ||
assertEquals(request.getPushType(), BATCH); | ||
assertEquals(request.getPushJobId(), "job123"); | ||
|
||
// Invalid clusterName | ||
IllegalArgumentException ex1 = Assert.expectThrows( | ||
IllegalArgumentException.class, | ||
() -> new RequestTopicForPushRequest("", "storeA", BATCH, "job123")); | ||
assertEquals(ex1.getMessage(), "clusterName is required"); | ||
|
||
// Invalid storeName | ||
IllegalArgumentException ex2 = Assert.expectThrows( | ||
IllegalArgumentException.class, | ||
() -> new RequestTopicForPushRequest("clusterA", "", BATCH, "job123")); | ||
assertEquals(ex2.getMessage(), "storeName is required"); | ||
|
||
// Null pushType | ||
IllegalArgumentException ex3 = Assert.expectThrows( | ||
IllegalArgumentException.class, | ||
() -> new RequestTopicForPushRequest("clusterA", "storeA", null, "job123")); | ||
assertEquals(ex3.getMessage(), "pushType is required"); | ||
|
||
// Invalid pushJobId | ||
IllegalArgumentException ex4 = Assert.expectThrows( | ||
IllegalArgumentException.class, | ||
() -> new RequestTopicForPushRequest("clusterA", "storeA", BATCH, "")); | ||
assertEquals(ex4.getMessage(), "pushJobId is required"); | ||
} | ||
|
||
@Test | ||
public void testExtractPushTypeValidAndInvalidValues() { | ||
// Valid cases | ||
assertEquals(RequestTopicForPushRequest.extractPushType("BATCH"), BATCH); | ||
assertEquals(RequestTopicForPushRequest.extractPushType("STREAM"), STREAM); | ||
|
||
// Invalid case | ||
IllegalArgumentException ex = Assert | ||
.expectThrows(IllegalArgumentException.class, () -> RequestTopicForPushRequest.extractPushType("INVALID")); | ||
assertTrue(ex.getMessage().contains("INVALID is an invalid push type")); | ||
} | ||
|
||
@Test | ||
public void testRequestTopicForPushRequestSettersAndGetters() { | ||
request.setSendStartOfPush(true); | ||
request.setSorted(true); | ||
request.setWriteComputeEnabled(true); | ||
request.setSourceGridFabric("fabricA"); | ||
request.setRewindTimeInSecondsOverride(3600); | ||
request.setDeferVersionSwap(true); | ||
request.setTargetedRegions("regionA,regionB"); | ||
request.setRepushSourceVersion(42); | ||
request.setPartitioners("partitioner1,partitioner2"); | ||
request.setCompressionDictionary("compressionDict"); | ||
request.setEmergencySourceRegion("regionX"); | ||
|
||
assertTrue(request.isSendStartOfPush()); | ||
assertTrue(request.isSorted()); | ||
assertTrue(request.isWriteComputeEnabled()); | ||
assertEquals(request.getSourceGridFabric(), "fabricA"); | ||
assertEquals(request.getRewindTimeInSecondsOverride(), 3600); | ||
assertTrue(request.isDeferVersionSwap()); | ||
assertEquals(request.getTargetedRegions(), "regionA,regionB"); | ||
assertEquals(request.getRepushSourceVersion(), 42); | ||
assertEquals(request.getPartitioners(), new HashSet<>(Arrays.asList("partitioner1", "partitioner2"))); | ||
assertEquals(request.getCompressionDictionary(), "compressionDict"); | ||
assertEquals(request.getEmergencySourceRegion(), "regionX"); | ||
} | ||
|
||
@Test | ||
public void testSetPartitionersValidAndEmptyCases() { | ||
// Valid partitioners | ||
request.setPartitioners("partitioner1"); | ||
assertEquals(request.getPartitioners(), new HashSet<>(Collections.singletonList("partitioner1"))); | ||
request.setPartitioners("partitioner1,partitioner2"); | ||
assertEquals(request.getPartitioners(), new HashSet<>(Arrays.asList("partitioner1", "partitioner2"))); | ||
|
||
// Empty set | ||
request.setPartitioners(Collections.emptySet()); | ||
assertEquals(request.getPartitioners(), Collections.emptySet()); | ||
|
||
// Null and empty string | ||
request.setPartitioners((String) null); | ||
assertEquals(request.getPartitioners(), Collections.emptySet()); | ||
|
||
request.setPartitioners(""); | ||
assertEquals(request.getPartitioners(), Collections.emptySet()); | ||
} | ||
} |
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.
I suspect there is a bug here in this function. I need to think through