chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
{auto_gen_header}
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>io.ray</groupId>
|
||||
<artifactId>ray-superpom</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ray-test</artifactId>
|
||||
<name>java test cases for ray</name>
|
||||
<description>java test cases for ray</description>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<maven.deploy.skip>true</maven.deploy.skip>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.ray</groupId>
|
||||
<artifactId>ray-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.ray</groupId>
|
||||
<artifactId>ray-runtime</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
{generated_bzl_deps}
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.21.0</version>
|
||||
<configuration>
|
||||
<suiteXmlFiles>
|
||||
<suiteXmlFile>../testng.xml</suiteXmlFile>
|
||||
</suiteXmlFiles>
|
||||
<trimStackTrace>false</trimStackTrace>
|
||||
<testSourceDirectory>${basedir}/src/main/java/</testSourceDirectory>
|
||||
<testClassesDirectory>${project.build.directory}/classes/</testClassesDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-dependencies</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${basedir}/lib</outputDirectory>
|
||||
<overWriteReleases>false</overWriteReleases>
|
||||
<overWriteSnapshots>false</overWriteSnapshots>
|
||||
<overWriteIfNewer>true</overWriteIfNewer>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,66 @@
|
||||
package io.ray.benchmark;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class ActorPressTest extends RayBenchmarkTest {
|
||||
|
||||
@Test
|
||||
public void singleLatencyTest() {
|
||||
int times = 10;
|
||||
ActorHandle<Adder> adder = Ray.actor(ActorPressTest.Adder::new).remote();
|
||||
super.singleLatencyTest(times, adder);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maxTest() {
|
||||
int clientNum = 2;
|
||||
int totalNum = 20;
|
||||
ActorHandle<Adder> adder = Ray.actor(ActorPressTest.Adder::new).remote();
|
||||
PressureTestParameter pressureTestParameter = new PressureTestParameter();
|
||||
pressureTestParameter.setClientNum(clientNum);
|
||||
pressureTestParameter.setTotalNum(totalNum);
|
||||
pressureTestParameter.setRayBenchmarkTest(this);
|
||||
pressureTestParameter.setRayActor(adder);
|
||||
super.maxPressureTest(pressureTestParameter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rateLimiterTest() {
|
||||
int clientNum = 2;
|
||||
int totalQps = 2;
|
||||
int duration = 3;
|
||||
ActorHandle<Adder> adder = Ray.actor(ActorPressTest.Adder::new).remote();
|
||||
PressureTestParameter pressureTestParameter = new PressureTestParameter();
|
||||
pressureTestParameter.setClientNum(clientNum);
|
||||
pressureTestParameter.setTotalQps(totalQps);
|
||||
pressureTestParameter.setDuration(duration);
|
||||
pressureTestParameter.setRayBenchmarkTest(this);
|
||||
pressureTestParameter.setRayActor(adder);
|
||||
super.rateLimiterPressureTest(pressureTestParameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectRef<RemoteResult<Integer>> rayCall(ActorHandle rayActor) {
|
||||
return ((ActorHandle<Adder>) rayActor).task(Adder::add, 10).remote();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkResult(Object o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static class Adder {
|
||||
|
||||
private Integer sum = 0;
|
||||
|
||||
public RemoteResult<Integer> add(Integer n) {
|
||||
RemoteResult<Integer> remoteResult = new RemoteResult<>();
|
||||
remoteResult.setResult(sum += n);
|
||||
remoteResult.setFinishTime(System.nanoTime());
|
||||
return remoteResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package io.ray.benchmark;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class MaxPressureTest extends RayBenchmarkTest {
|
||||
|
||||
public static final int clientNum = 2;
|
||||
public static final int totalNum = 10;
|
||||
private static final long serialVersionUID = -1684518885171395952L;
|
||||
|
||||
public static RemoteResult<Integer> currentTime() {
|
||||
RemoteResult<Integer> remoteResult = new RemoteResult<>();
|
||||
remoteResult.setFinishTime(System.nanoTime());
|
||||
remoteResult.setResult(0);
|
||||
return remoteResult;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
PressureTestParameter pressureTestParameter = new PressureTestParameter();
|
||||
pressureTestParameter.setClientNum(clientNum);
|
||||
pressureTestParameter.setTotalNum(totalNum);
|
||||
pressureTestParameter.setRayBenchmarkTest(this);
|
||||
super.maxPressureTest(pressureTestParameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectRef<RemoteResult<Integer>> rayCall(ActorHandle rayActor) {
|
||||
|
||||
return Ray.task(MaxPressureTest::currentTime).remote();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkResult(Object o) {
|
||||
return (int) o == 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package io.ray.benchmark;
|
||||
|
||||
import io.ray.api.Ray;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class MicroBenchmarks {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MicroBenchmarks.class);
|
||||
|
||||
public static Object simpleFunction() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void time(Runnable runnable, int numRepeats, String name) {
|
||||
LOGGER.info("Benchmark \"{}\" started.", name);
|
||||
final long start = System.nanoTime();
|
||||
for (int i = 0; i < numRepeats; i++) {
|
||||
runnable.run();
|
||||
}
|
||||
final long duration = System.nanoTime() - start;
|
||||
LOGGER.info(
|
||||
"Benchmark \"{}\" finished, repeated {} times, total duration {} ms,"
|
||||
+ " average duration {} ns.",
|
||||
name,
|
||||
numRepeats,
|
||||
duration / 1_000_000,
|
||||
duration / numRepeats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Benchmark task submission.
|
||||
*
|
||||
* <p>Note, this benchmark is supposed to measure the elapased time in Java worker, we should
|
||||
* disable submitting tasks to raylet in `raylet_client.cc` before running this benchmark.
|
||||
*/
|
||||
public static void benchmarkTaskSubmission() {
|
||||
final int numRepeats = 1_000_000;
|
||||
Ray.init();
|
||||
try {
|
||||
time(
|
||||
() -> {
|
||||
Ray.task(MicroBenchmarks::simpleFunction).remote();
|
||||
},
|
||||
numRepeats,
|
||||
"task submission");
|
||||
} finally {
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
benchmarkTaskSubmission();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package io.ray.benchmark;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import java.io.Serializable;
|
||||
|
||||
public class PressureTestParameter implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -52054601722982473L;
|
||||
|
||||
private Integer clientNum = 1; // number of test client
|
||||
|
||||
private PressureTestType pressureTestType = PressureTestType.RATE_LIMITER; // pressure test type
|
||||
|
||||
private Integer totalNum = 1; // total number of task under the mode of MAX
|
||||
|
||||
private Integer totalQps = 1; // total qps of task under the mode of RATE_LIMITER
|
||||
|
||||
private Integer duration = 1; // duration of the pressure test under the mode of RATE_LIMITER
|
||||
|
||||
private RayBenchmarkTest rayBenchmarkTest; // reference of current test case instance
|
||||
|
||||
// reference of the Actor, if only test remote function it could be null
|
||||
private ActorHandle rayActor;
|
||||
|
||||
public Integer getClientNum() {
|
||||
return clientNum;
|
||||
}
|
||||
|
||||
public void setClientNum(Integer clientNum) {
|
||||
this.clientNum = clientNum;
|
||||
}
|
||||
|
||||
public PressureTestType getPressureTestType() {
|
||||
return pressureTestType;
|
||||
}
|
||||
|
||||
public void setPressureTestType(PressureTestType pressureTestType) {
|
||||
this.pressureTestType = pressureTestType;
|
||||
}
|
||||
|
||||
public Integer getTotalNum() {
|
||||
return totalNum;
|
||||
}
|
||||
|
||||
public void setTotalNum(Integer totalNum) {
|
||||
this.totalNum = totalNum;
|
||||
}
|
||||
|
||||
public Integer getTotalQps() {
|
||||
return totalQps;
|
||||
}
|
||||
|
||||
public void setTotalQps(Integer totalQps) {
|
||||
this.totalQps = totalQps;
|
||||
}
|
||||
|
||||
public Integer getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public void setDuration(Integer duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
public RayBenchmarkTest getRayBenchmarkTest() {
|
||||
return rayBenchmarkTest;
|
||||
}
|
||||
|
||||
public void setRayBenchmarkTest(RayBenchmarkTest rayBenchmarkTest) {
|
||||
this.rayBenchmarkTest = rayBenchmarkTest;
|
||||
}
|
||||
|
||||
public ActorHandle getRayActor() {
|
||||
return rayActor;
|
||||
}
|
||||
|
||||
public void setRayActor(ActorHandle rayActor) {
|
||||
this.rayActor = rayActor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package io.ray.benchmark;
|
||||
|
||||
public enum PressureTestType {
|
||||
SINGLE_LATENCY,
|
||||
RATE_LIMITER,
|
||||
MAX
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package io.ray.benchmark;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class RateLimiterPressureTest extends RayBenchmarkTest {
|
||||
|
||||
public static final int clientNum = 2;
|
||||
public static final int totalQps = 2;
|
||||
public static final int duration = 10;
|
||||
private static final long serialVersionUID = 6616958120966144235L;
|
||||
|
||||
public static RemoteResult<Integer> currentTime() {
|
||||
RemoteResult<Integer> remoteResult = new RemoteResult<>();
|
||||
remoteResult.setFinishTime(System.nanoTime());
|
||||
remoteResult.setResult(0);
|
||||
return remoteResult;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
PressureTestParameter pressureTestParameter = new PressureTestParameter();
|
||||
pressureTestParameter.setClientNum(clientNum);
|
||||
pressureTestParameter.setTotalQps(totalQps);
|
||||
pressureTestParameter.setDuration(duration);
|
||||
pressureTestParameter.setRayBenchmarkTest(this);
|
||||
super.rateLimiterPressureTest(pressureTestParameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectRef<RemoteResult<Integer>> rayCall(ActorHandle rayActor) {
|
||||
|
||||
return Ray.task(RateLimiterPressureTest::currentTime).remote();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkResult(Object o) {
|
||||
return (int) o == 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package io.ray.benchmark;
|
||||
|
||||
import com.google.common.util.concurrent.RateLimiter;
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.function.RayFunc1;
|
||||
import io.ray.test.BaseTest;
|
||||
import java.io.Serializable;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testng.Assert;
|
||||
|
||||
public abstract class RayBenchmarkTest<T> extends BaseTest implements Serializable {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RayBenchmarkTest.class);
|
||||
// not thread safe ,but we only have one thread here
|
||||
public static final DecimalFormat df = new DecimalFormat("00.00");
|
||||
private static final long serialVersionUID = 416045641835782523L;
|
||||
|
||||
private static List<Long> singleClient(PressureTestParameter pressureTestParameter) {
|
||||
|
||||
try {
|
||||
List<Long> counterList = new ArrayList<>();
|
||||
PressureTestType pressureTestType = pressureTestParameter.getPressureTestType();
|
||||
RayBenchmarkTest rayBenchmarkTest = pressureTestParameter.getRayBenchmarkTest();
|
||||
int clientNum = pressureTestParameter.getClientNum();
|
||||
// SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
int len;
|
||||
String logPrefix;
|
||||
RateLimiter rateLimiter = null;
|
||||
if (pressureTestType.equals(PressureTestType.MAX)) {
|
||||
len = pressureTestParameter.getTotalNum() / clientNum;
|
||||
logPrefix = "MAX";
|
||||
} else {
|
||||
int totalQps = pressureTestParameter.getTotalQps();
|
||||
int duration = pressureTestParameter.getDuration();
|
||||
int qps = totalQps / clientNum;
|
||||
rateLimiter = RateLimiter.create(qps);
|
||||
len = qps * duration;
|
||||
logPrefix = "RATE_LIMITER";
|
||||
}
|
||||
RemoteResultWrapper[] remoteResultWrappers = new RemoteResultWrapper[len];
|
||||
int i = 0;
|
||||
while (i < len) {
|
||||
if (rateLimiter != null) {
|
||||
rateLimiter.acquire();
|
||||
}
|
||||
RemoteResultWrapper temp = new RemoteResultWrapper();
|
||||
temp.setStartTime(System.nanoTime());
|
||||
temp.setObjectRef(rayBenchmarkTest.rayCall(pressureTestParameter.getRayActor()));
|
||||
remoteResultWrappers[i++] = temp;
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
while (j < len) {
|
||||
RemoteResultWrapper temp = remoteResultWrappers[j++];
|
||||
RemoteResult remoteResult = (RemoteResult) temp.getObjectRef().get();
|
||||
long endTime = remoteResult.getFinishTime();
|
||||
long costTime = endTime - temp.getStartTime();
|
||||
counterList.add(costTime / 1000);
|
||||
LOGGER.warn("{}_cost_time:{}ns", logPrefix, costTime);
|
||||
Assert.assertTrue(rayBenchmarkTest.checkResult(remoteResult.getResult()));
|
||||
}
|
||||
return counterList;
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("singleClient", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void singleLatencyTest(int times, ActorHandle rayActor) {
|
||||
|
||||
List<Long> counterList = new ArrayList<>();
|
||||
for (int i = 0; i < times; i++) {
|
||||
long startTime = System.nanoTime();
|
||||
ObjectRef<RemoteResult<T>> objectRef = rayCall(rayActor);
|
||||
RemoteResult<T> remoteResult = objectRef.get();
|
||||
T t = remoteResult.getResult();
|
||||
long endTime = System.nanoTime();
|
||||
long costTime = endTime - startTime;
|
||||
counterList.add(costTime / 1000);
|
||||
LOGGER.warn("SINGLE_LATENCY_cost_time: {} us", costTime);
|
||||
Assert.assertTrue(checkResult(t));
|
||||
}
|
||||
Collections.sort(counterList);
|
||||
printList(counterList);
|
||||
}
|
||||
|
||||
public abstract ObjectRef<RemoteResult<T>> rayCall(ActorHandle rayActor);
|
||||
|
||||
public abstract boolean checkResult(T t);
|
||||
|
||||
private void printList(List<Long> list) {
|
||||
int len = list.size();
|
||||
int middle = len / 2;
|
||||
int almostHundred = (int) (len * 0.9999);
|
||||
int ninetyNine = (int) (len * 0.99);
|
||||
int ninetyFive = (int) (len * 0.95);
|
||||
int ninety = (int) (len * 0.9);
|
||||
int fifty = (int) (len * 0.5);
|
||||
|
||||
LOGGER.error("Final result of rt as below:");
|
||||
LOGGER.error("max: {}μs", list.get(len - 1));
|
||||
LOGGER.error("min: {}μs", list.get(0));
|
||||
LOGGER.error("median: {}μs", list.get(middle));
|
||||
LOGGER.error("99.99% data smaller than: {}μs", list.get(almostHundred));
|
||||
LOGGER.error("99% data smaller than: {}μs", list.get(ninetyNine));
|
||||
LOGGER.error("95% data smaller than: {}μs", list.get(ninetyFive));
|
||||
LOGGER.error("90% data smaller than: {}μs", list.get(ninety));
|
||||
LOGGER.error("50% data smaller than: {}μs", list.get(fifty));
|
||||
}
|
||||
|
||||
public void rateLimiterPressureTest(PressureTestParameter pressureTestParameter) {
|
||||
|
||||
pressureTestParameter.setPressureTestType(PressureTestType.RATE_LIMITER);
|
||||
notSinglePressTest(pressureTestParameter);
|
||||
}
|
||||
|
||||
private void notSinglePressTest(PressureTestParameter pressureTestParameter) {
|
||||
|
||||
List<Long> counterList = new ArrayList<>();
|
||||
int clientNum = pressureTestParameter.getClientNum();
|
||||
ObjectRef<List<Long>>[] objectRefs = new ObjectRef[clientNum];
|
||||
|
||||
for (int i = 0; i < clientNum; i++) {
|
||||
// Java compiler can't automatically infer the type of
|
||||
// `RayBenchmarkTest::singleClient`, because `RayBenchmarkTest` is a generic class.
|
||||
// It will match both `RayFunc1` and `RayFuncVoid1`. This looks like a bug or
|
||||
// defect of the Java compiler.
|
||||
// TODO(hchen): Figure out how to avoid manually declaring `RayFunc` type in this case.
|
||||
RayFunc1<PressureTestParameter, List<Long>> func = RayBenchmarkTest::singleClient;
|
||||
objectRefs[i] = Ray.task(func, pressureTestParameter).remote();
|
||||
}
|
||||
for (int i = 0; i < clientNum; i++) {
|
||||
List<Long> subCounterList = objectRefs[i].get();
|
||||
Assert.assertNotNull(subCounterList);
|
||||
counterList.addAll(subCounterList);
|
||||
}
|
||||
Collections.sort(counterList);
|
||||
printList(counterList);
|
||||
}
|
||||
|
||||
public void maxPressureTest(PressureTestParameter pressureTestParameter) {
|
||||
|
||||
pressureTestParameter.setPressureTestType(PressureTestType.MAX);
|
||||
notSinglePressTest(pressureTestParameter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.ray.benchmark;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class RemoteResult<T> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3825949468039358540L;
|
||||
|
||||
private long finishTime;
|
||||
|
||||
private T result;
|
||||
|
||||
public long getFinishTime() {
|
||||
return finishTime;
|
||||
}
|
||||
|
||||
public void setFinishTime(long finishTime) {
|
||||
this.finishTime = finishTime;
|
||||
}
|
||||
|
||||
public T getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setResult(T result) {
|
||||
this.result = result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package io.ray.benchmark;
|
||||
|
||||
import io.ray.api.ObjectRef;
|
||||
|
||||
public class RemoteResultWrapper<T> {
|
||||
|
||||
private long startTime;
|
||||
|
||||
private ObjectRef<RemoteResult<T>> objectRef;
|
||||
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setStartTime(long startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public ObjectRef<RemoteResult<T>> getObjectRef() {
|
||||
return objectRef;
|
||||
}
|
||||
|
||||
public void setObjectRef(ObjectRef<RemoteResult<T>> objectRef) {
|
||||
this.objectRef = objectRef;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package io.ray.benchmark;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class SingleLatencyTest extends RayBenchmarkTest {
|
||||
|
||||
public static final int totalNum = 10;
|
||||
private static final long serialVersionUID = 3559601273941694468L;
|
||||
|
||||
public static RemoteResult<Integer> doFunc() {
|
||||
RemoteResult<Integer> remoteResult = new RemoteResult<>();
|
||||
remoteResult.setResult(1);
|
||||
return remoteResult;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
super.singleLatencyTest(totalNum, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectRef<RemoteResult<Integer>> rayCall(ActorHandle rayActor) {
|
||||
return Ray.task(SingleLatencyTest::doFunc).remote();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkResult(Object o) {
|
||||
return (int) o == 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package io.ray.docdemo;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.PlacementGroups;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.WaitResult;
|
||||
import io.ray.api.options.PlacementGroupCreationOptions;
|
||||
import io.ray.api.placementgroup.PlacementGroup;
|
||||
import io.ray.api.placementgroup.PlacementGroupState;
|
||||
import io.ray.api.placementgroup.PlacementStrategy;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.testng.Assert;
|
||||
|
||||
public class PlacementGroupDemo {
|
||||
|
||||
public static class Counter {
|
||||
private int value;
|
||||
|
||||
public Counter(int initValue) {
|
||||
this.value = initValue;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static String ping() {
|
||||
return "pong";
|
||||
}
|
||||
}
|
||||
|
||||
public static void createAndRemovePlacementGroup() {
|
||||
// Construct a list of bundles.
|
||||
Map<String, Double> bundle = ImmutableMap.of("CPU", 1.0);
|
||||
List<Map<String, Double>> bundles = ImmutableList.of(bundle);
|
||||
|
||||
// Make a creation option with bundles and strategy.
|
||||
PlacementGroupCreationOptions options =
|
||||
new PlacementGroupCreationOptions.Builder()
|
||||
.setBundles(bundles)
|
||||
.setStrategy(PlacementStrategy.STRICT_SPREAD)
|
||||
.build();
|
||||
|
||||
PlacementGroup pg = PlacementGroups.createPlacementGroup(options);
|
||||
|
||||
// Wait for the placement group to be ready within the specified time(unit is seconds).
|
||||
boolean ready = pg.wait(60);
|
||||
Assert.assertTrue(ready);
|
||||
|
||||
// You can look at placement group states using this API.
|
||||
List<PlacementGroup> allPlacementGroup = PlacementGroups.getAllPlacementGroups();
|
||||
for (PlacementGroup group : allPlacementGroup) {
|
||||
System.out.println(group);
|
||||
}
|
||||
|
||||
PlacementGroups.removePlacementGroup(pg.getId());
|
||||
|
||||
PlacementGroup removedPlacementGroup = PlacementGroups.getPlacementGroup(pg.getId());
|
||||
Assert.assertEquals(removedPlacementGroup.getState(), PlacementGroupState.REMOVED);
|
||||
}
|
||||
|
||||
public static void runNormalTaskWithPlacementGroup() {
|
||||
// Construct a list of bundles.
|
||||
Map<String, Double> bundle = ImmutableMap.of("CPU", 2.0);
|
||||
List<Map<String, Double>> bundles = ImmutableList.of(bundle);
|
||||
|
||||
// Create a placement group and make sure its creation is successful.
|
||||
PlacementGroupCreationOptions options =
|
||||
new PlacementGroupCreationOptions.Builder()
|
||||
.setBundles(bundles)
|
||||
.setStrategy(PlacementStrategy.STRICT_SPREAD)
|
||||
.build();
|
||||
|
||||
PlacementGroup pg = PlacementGroups.createPlacementGroup(options);
|
||||
boolean isCreated = pg.wait(60);
|
||||
Assert.assertTrue(isCreated);
|
||||
|
||||
// Won't be scheduled because there are no 2 cpus now.
|
||||
ObjectRef<String> obj = Ray.task(Counter::ping).setResource("CPU", 2.0).remote();
|
||||
|
||||
List<ObjectRef<String>> waitList = ImmutableList.of(obj);
|
||||
WaitResult<String> waitResult = Ray.wait(waitList, 1, 5 * 1000);
|
||||
Assert.assertEquals(1, waitResult.getUnready().size());
|
||||
|
||||
// Will be scheduled because 2 cpus are reserved by the placement group.
|
||||
obj = Ray.task(Counter::ping).setPlacementGroup(pg, 0).setResource("CPU", 2.0).remote();
|
||||
Assert.assertEquals(obj.get(), "pong");
|
||||
|
||||
PlacementGroups.removePlacementGroup(pg.getId());
|
||||
|
||||
PlacementGroup removedPlacementGroup = PlacementGroups.getPlacementGroup(pg.getId());
|
||||
Assert.assertEquals(removedPlacementGroup.getState(), PlacementGroupState.REMOVED);
|
||||
}
|
||||
|
||||
public static void createNonGlobalNamedPlacementGroup() {
|
||||
// Create a placement group with a job-scope-unique name.
|
||||
Map<String, Double> bundle = ImmutableMap.of("CPU", 1.0);
|
||||
List<Map<String, Double>> bundles = ImmutableList.of(bundle);
|
||||
|
||||
PlacementGroupCreationOptions options =
|
||||
new PlacementGroupCreationOptions.Builder()
|
||||
.setBundles(bundles)
|
||||
.setStrategy(PlacementStrategy.STRICT_SPREAD)
|
||||
.setName("non_global_name")
|
||||
.build();
|
||||
|
||||
PlacementGroup pg = PlacementGroups.createPlacementGroup(options);
|
||||
pg.wait(60);
|
||||
|
||||
// Retrieve the placement group later somewhere in the same job.
|
||||
PlacementGroup group = PlacementGroups.getPlacementGroup("non_global_name");
|
||||
Assert.assertNotNull(group);
|
||||
|
||||
PlacementGroups.removePlacementGroup(pg.getId());
|
||||
|
||||
PlacementGroup removedPlacementGroup = PlacementGroups.getPlacementGroup(pg.getId());
|
||||
Assert.assertEquals(removedPlacementGroup.getState(), PlacementGroupState.REMOVED);
|
||||
}
|
||||
|
||||
public static void strictPackExample() {
|
||||
Map<String, Double> bundle1 = ImmutableMap.of("GPU", 2.0);
|
||||
Map<String, Double> bundle2 = ImmutableMap.of("extra_resource", 2.0);
|
||||
List<Map<String, Double>> bundles = ImmutableList.of(bundle1, bundle2);
|
||||
|
||||
PlacementGroupCreationOptions options =
|
||||
new PlacementGroupCreationOptions.Builder()
|
||||
.setBundles(bundles)
|
||||
.setStrategy(PlacementStrategy.STRICT_PACK)
|
||||
.build();
|
||||
|
||||
PlacementGroup pg = PlacementGroups.createPlacementGroup(options);
|
||||
boolean isCreated = pg.wait(60);
|
||||
Assert.assertTrue(isCreated);
|
||||
|
||||
// Create GPU actors on a gpu bundle.
|
||||
for (int index = 0; index < 2; index++) {
|
||||
Ray.actor(Counter::new, 1).setResource("GPU", 1.0).setPlacementGroup(pg, 0).remote();
|
||||
}
|
||||
|
||||
// Create extra_resource actors on a extra_resource bundle.
|
||||
for (int index = 0; index < 2; index++) {
|
||||
Ray.task(Counter::ping)
|
||||
.setPlacementGroup(pg, 1)
|
||||
.setResource("extra_resource", 1.0)
|
||||
.remote()
|
||||
.get();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Start Ray runtime. If you're connecting to an existing cluster, you can set
|
||||
// the `-Dray.address=<cluster-address>` java system property.
|
||||
System.setProperty("ray.head-args.0", "--resources={\"extra_resource\":2.0}");
|
||||
System.setProperty("ray.head-args.1", "--num-cpus=2");
|
||||
System.setProperty("ray.head-args.2", "--num-gpus=2");
|
||||
Ray.init();
|
||||
|
||||
createAndRemovePlacementGroup();
|
||||
|
||||
runNormalTaskWithPlacementGroup();
|
||||
|
||||
createNonGlobalNamedPlacementGroup();
|
||||
|
||||
strictPackExample();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package io.ray.docdemo;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* This class contains demo code of the Ray introduction doc
|
||||
* (https://docs.ray.io/en/master/index.html and
|
||||
* https://docs.ray.io/en/master/ray-overview/index.html).
|
||||
*
|
||||
* <p>Please keep them in sync.
|
||||
*/
|
||||
public class RayDemo {
|
||||
|
||||
public static int square(int x) {
|
||||
return x * x;
|
||||
}
|
||||
|
||||
public static class Counter {
|
||||
|
||||
private int value = 0;
|
||||
|
||||
public void increment() {
|
||||
this.value += 1;
|
||||
}
|
||||
|
||||
public int read() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Initialize Ray runtime.
|
||||
Ray.init();
|
||||
{
|
||||
List<ObjectRef<Integer>> objectRefList = new ArrayList<>();
|
||||
// Invoke the `square` method 4 times remotely as Ray tasks.
|
||||
// The tasks will run in parallel in the background.
|
||||
for (int i = 0; i < 4; i++) {
|
||||
objectRefList.add(Ray.task(RayDemo::square, i).remote());
|
||||
}
|
||||
// Get the actual results of the tasks with `get`.
|
||||
System.out.println(Ray.get(objectRefList)); // [0, 1, 4, 9]
|
||||
}
|
||||
|
||||
{
|
||||
List<ActorHandle<Counter>> counters = new ArrayList<>();
|
||||
// Create 4 actors from the `Counter` class.
|
||||
// They will run in remote worker processes.
|
||||
for (int i = 0; i < 4; i++) {
|
||||
counters.add(Ray.actor(Counter::new).remote());
|
||||
}
|
||||
|
||||
// Invoke the `increment` method on each actor.
|
||||
// This will send an actor task to each remote actor.
|
||||
for (ActorHandle<Counter> counter : counters) {
|
||||
counter.task(Counter::increment).remote();
|
||||
}
|
||||
// Invoke the `read` method on each actor, and print the results.
|
||||
List<ObjectRef<Integer>> objectRefList =
|
||||
counters.stream()
|
||||
.map(counter -> counter.task(Counter::read).remote())
|
||||
.collect(Collectors.toList());
|
||||
System.out.println(Ray.get(objectRefList)); // [1, 1, 1, 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package io.ray.docdemo;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.function.RayFunc1;
|
||||
import io.ray.api.function.RayFunc2;
|
||||
import io.ray.api.function.RayFunc3;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.testng.Assert;
|
||||
|
||||
/**
|
||||
* This class contains demo code of the Ray core Using Actors doc
|
||||
* (https://docs.ray.io/en/master/actors.html).
|
||||
*
|
||||
* <p>Please keep them in sync.
|
||||
*/
|
||||
public class UsingActorsDemo {
|
||||
|
||||
// A regular Java class.
|
||||
public static class Counter {
|
||||
|
||||
private int value = 0;
|
||||
|
||||
public int increment() {
|
||||
this.value += 1;
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public int getCounter() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public void reset(int newValue) {
|
||||
this.value = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CounterOverloaded extends Counter {
|
||||
public int increment(int diff) {
|
||||
super.value += diff;
|
||||
return super.value;
|
||||
}
|
||||
|
||||
public int increment(int diff1, int diff2) {
|
||||
super.value += diff1 + diff2;
|
||||
return super.value;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CounterFactory {
|
||||
|
||||
public static Counter createCounter() {
|
||||
return new Counter();
|
||||
}
|
||||
}
|
||||
|
||||
public static class GpuActor {}
|
||||
|
||||
public static class MyRayApp {
|
||||
|
||||
public static void foo(ActorHandle<Counter> counter) throws InterruptedException {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
TimeUnit.MILLISECONDS.sleep(100);
|
||||
counter.task(Counter::increment).remote();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
Ray.init();
|
||||
|
||||
{
|
||||
// Create an actor with a constructor.
|
||||
Ray.actor(Counter::new).remote();
|
||||
// Create an actor with a factory method.
|
||||
Ray.actor(CounterFactory::createCounter).remote();
|
||||
}
|
||||
|
||||
{
|
||||
ActorHandle<Counter> a = Ray.actor(Counter::new).remote();
|
||||
// Call an actor method with a return value
|
||||
Assert.assertEquals((int) a.task(Counter::increment).remote().get(), 1);
|
||||
// Call an actor method without return value
|
||||
a.task(Counter::reset, 10).remote();
|
||||
Assert.assertEquals((int) a.task(Counter::increment).remote().get(), 11);
|
||||
}
|
||||
|
||||
{
|
||||
ActorHandle<CounterOverloaded> a = Ray.actor(CounterOverloaded::new).remote();
|
||||
// Call an overloaded actor method by super class method reference.
|
||||
Assert.assertEquals((int) a.task(Counter::increment).remote().get(), 1);
|
||||
// Call an overloaded actor method, cast method reference first.
|
||||
a.task((RayFunc1<CounterOverloaded, Integer>) CounterOverloaded::increment).remote();
|
||||
a.task((RayFunc2<CounterOverloaded, Integer, Integer>) CounterOverloaded::increment, 10)
|
||||
.remote();
|
||||
RayFunc3<CounterOverloaded, Integer, Integer, Integer> f = CounterOverloaded::increment;
|
||||
a.task(f, 10, 10).remote();
|
||||
Assert.assertEquals((int) a.task(Counter::increment).remote().get(), 33);
|
||||
}
|
||||
|
||||
{
|
||||
Ray.actor(GpuActor::new).setResource("CPU", 2.0).setResource("GPU", 0.5).remote();
|
||||
}
|
||||
|
||||
{
|
||||
Ray.actor(GpuActor::new).setResource("Resource2", 1.0).remote();
|
||||
}
|
||||
|
||||
{
|
||||
ActorHandle<Counter> a1 =
|
||||
Ray.actor(Counter::new).setResource("CPU", 1.0).setResource("Custom1", 1.0).remote();
|
||||
ActorHandle<Counter> a2 =
|
||||
Ray.actor(Counter::new).setResource("CPU", 2.0).setResource("Custom2", 1.0).remote();
|
||||
ActorHandle<Counter> a3 =
|
||||
Ray.actor(Counter::new).setResource("CPU", 3.0).setResource("Custom3", 1.0).remote();
|
||||
}
|
||||
|
||||
{
|
||||
ActorHandle<Counter> actorHandle = Ray.actor(Counter::new).remote();
|
||||
actorHandle.kill();
|
||||
}
|
||||
|
||||
{
|
||||
// Create an actor with a job-scope-unique name
|
||||
ActorHandle<Counter> counter = Ray.actor(Counter::new).setName("some_name_in_job").remote();
|
||||
}
|
||||
{
|
||||
// Retrieve the actor later somewhere in the same job
|
||||
Optional<ActorHandle<Counter>> counter = Ray.getActor("some_name_in_job");
|
||||
Assert.assertTrue(counter.isPresent());
|
||||
}
|
||||
|
||||
{
|
||||
ActorHandle<Counter> counter = Ray.actor(Counter::new).remote();
|
||||
|
||||
// Start some tasks that use the actor.
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Ray.task(MyRayApp::foo, counter).remote();
|
||||
}
|
||||
|
||||
// Print the counter value.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
System.out.println(counter.task(Counter::getCounter).remote().get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package io.ray.docdemo;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.WaitResult;
|
||||
import io.ray.api.function.RayFunc0;
|
||||
import io.ray.api.function.RayFunc1;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.testng.Assert;
|
||||
|
||||
/**
|
||||
* This class contains demo code of the Ray core walkthrough doc
|
||||
* (https://docs.ray.io/en/master/walkthrough.html).
|
||||
*
|
||||
* <p>Please keep them in sync.
|
||||
*/
|
||||
public class WalkthroughDemo {
|
||||
|
||||
public static class MyRayApp {
|
||||
|
||||
// A regular Java static method.
|
||||
public static int myFunction() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static int slowFunction() throws InterruptedException {
|
||||
TimeUnit.SECONDS.sleep(10);
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static int functionWithAnArgument(int value) {
|
||||
return value + 1;
|
||||
}
|
||||
|
||||
public static int overloadFunction() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static int overloadFunction(int x) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
public static void demoTasks() {
|
||||
// Invoke the above method as a Ray remote function.
|
||||
// This will immediately return an object ref (a future) and then create
|
||||
// a task that will be executed on a worker process.
|
||||
ObjectRef<Integer> res = Ray.task(MyRayApp::myFunction).remote();
|
||||
|
||||
// The result can be retrieved with ``ObjectRef::get``.
|
||||
Assert.assertTrue(res.get() == 1);
|
||||
|
||||
// Invocations of Ray remote functions happen in parallel.
|
||||
// All computation is performed in the background, driven by Ray's internal event loop.
|
||||
for (int i = 0; i < 4; i++) {
|
||||
// This doesn't block.
|
||||
Ray.task(MyRayApp::slowFunction).remote();
|
||||
}
|
||||
|
||||
// Invoke overloaded functions.
|
||||
Assert.assertEquals(
|
||||
(int) Ray.task((RayFunc0<Integer>) MyRayApp::overloadFunction).remote().get(), 1);
|
||||
Assert.assertEquals(
|
||||
(int) Ray.task((RayFunc1<Integer, Integer>) MyRayApp::overloadFunction, 2).remote().get(),
|
||||
2);
|
||||
|
||||
ObjectRef<Integer> objRef1 = Ray.task(MyRayApp::myFunction).remote();
|
||||
Assert.assertTrue(objRef1.get() == 1);
|
||||
|
||||
// You can pass an `ObjectRef` as an argument to another Ray remote function.
|
||||
ObjectRef<Integer> objRef2 = Ray.task(MyRayApp::functionWithAnArgument, objRef1).remote();
|
||||
Assert.assertTrue(objRef2.get() == 2);
|
||||
|
||||
// Specify required resources.
|
||||
Ray.task(MyRayApp::myFunction).setResource("CPU", 2.0).setResource("GPU", 4.0).remote();
|
||||
|
||||
// Ray aslo supports fractional and custom resources.
|
||||
Ray.task(MyRayApp::myFunction).setResource("GPU", 0.5).setResource("Custom", 1.0).remote();
|
||||
}
|
||||
|
||||
public static void demoObjects() {
|
||||
// Put an object in Ray's object store.
|
||||
int y = 1;
|
||||
ObjectRef<Integer> objectRef = Ray.put(y);
|
||||
// Get the value of one object ref.
|
||||
Assert.assertTrue(objectRef.get() == 1);
|
||||
|
||||
// Get the values of multiple object refs in parallel.
|
||||
List<ObjectRef<Integer>> objectRefs = new ArrayList<>();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
objectRefs.add(Ray.put(i));
|
||||
}
|
||||
List<Integer> results = Ray.get(objectRefs);
|
||||
Assert.assertEquals(results, ImmutableList.of(0, 1, 2));
|
||||
|
||||
WaitResult<Integer> waitResult = Ray.wait(objectRefs, /*num_returns=*/ 1, /*timeoutMs=*/ 1000);
|
||||
System.out.println(waitResult.getReady()); // List of ready objects.
|
||||
System.out.println(waitResult.getUnready()); // list of unready objects.
|
||||
}
|
||||
|
||||
// A regular Java class.
|
||||
public static class Counter {
|
||||
|
||||
private int value = 0;
|
||||
|
||||
public int increment() {
|
||||
this.value += 1;
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
public static void demoActors() {
|
||||
// Create an actor from this class.
|
||||
// `Ray.actor` takes a factory method that can produce
|
||||
// a `Counter` object. Here, we pass `Counter`'s constructor
|
||||
// as the argument.
|
||||
ActorHandle<Counter> counter = Ray.actor(Counter::new).remote();
|
||||
|
||||
// Specify required resources for an actor.
|
||||
Ray.actor(Counter::new).setResource("CPU", 2.0).setResource("GPU", 0.5).remote();
|
||||
|
||||
// Call the actor.
|
||||
ObjectRef<Integer> objectRef = counter.task(Counter::increment).remote();
|
||||
Assert.assertTrue(objectRef.get() == 1);
|
||||
|
||||
// Create ten Counter actors.
|
||||
List<ActorHandle<Counter>> counters = new ArrayList<>();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
counters.add(Ray.actor(Counter::new).remote());
|
||||
}
|
||||
|
||||
// Increment each Counter once and get the results. These tasks all happen in
|
||||
// parallel.
|
||||
List<ObjectRef<Integer>> objectRefs = new ArrayList<>();
|
||||
for (ActorHandle<Counter> counterActor : counters) {
|
||||
objectRefs.add(counterActor.task(Counter::increment).remote());
|
||||
}
|
||||
// prints [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||
System.out.println(Ray.get(objectRefs));
|
||||
|
||||
// Increment the first Counter five times. These tasks are executed serially
|
||||
// and share state.
|
||||
objectRefs = new ArrayList<>();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
objectRefs.add(counters.get(0).task(Counter::increment).remote());
|
||||
}
|
||||
// prints [2, 3, 4, 5, 6]
|
||||
System.out.println(Ray.get(objectRefs));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Start Ray runtime. If you're connecting to an existing cluster, you can set
|
||||
// the `-Dray.address=<cluster-address>` java system property.
|
||||
Ray.init();
|
||||
|
||||
demoTasks();
|
||||
|
||||
demoObjects();
|
||||
|
||||
demoActors();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test
|
||||
public class ActorConcurrentCallTest extends BaseTest {
|
||||
|
||||
public static class ConcurrentActor {
|
||||
private final CountDownLatch countDownLatch = new CountDownLatch(3);
|
||||
|
||||
public String countDown() {
|
||||
countDownLatch.countDown();
|
||||
try {
|
||||
countDownLatch.await();
|
||||
return "ok";
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testConcurrentCall() {
|
||||
ActorHandle<ConcurrentActor> actor =
|
||||
Ray.actor(ConcurrentActor::new).setMaxConcurrency(3).remote();
|
||||
ObjectRef<String> obj1 = actor.task(ConcurrentActor::countDown).remote();
|
||||
ObjectRef<String> obj2 = actor.task(ConcurrentActor::countDown).remote();
|
||||
ObjectRef<String> obj3 = actor.task(ConcurrentActor::countDown).remote();
|
||||
|
||||
Assert.assertEquals(obj1.get(), "ok");
|
||||
Assert.assertEquals(obj2.get(), "ok");
|
||||
Assert.assertEquals(obj3.get(), "ok");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.runtime.actor.NativeActorHandle;
|
||||
import io.ray.runtime.util.SystemUtil;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Set;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class ActorHandleReferenceCountTest {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ActorHandleReferenceCountTest.class);
|
||||
|
||||
/**
|
||||
* Because we can't explicitly GC an Java object. We use this helper method to manually remove an
|
||||
* local reference.
|
||||
*/
|
||||
private static void del(ActorHandle<?> handle) {
|
||||
try {
|
||||
Field referencesField = NativeActorHandle.class.getDeclaredField("REFERENCES");
|
||||
referencesField.setAccessible(true);
|
||||
Set<?> references = (Set<?>) referencesField.get(null);
|
||||
Class<?> referenceClass =
|
||||
Class.forName("io.ray.runtime.actor.NativeActorHandle$NativeActorHandleReference");
|
||||
Method finalizeReferentMethod = referenceClass.getDeclaredMethod("finalizeReferent");
|
||||
finalizeReferentMethod.setAccessible(true);
|
||||
for (Object reference : references) {
|
||||
if (handle.equals(((Reference<?>) reference).get())) {
|
||||
finalizeReferentMethod.invoke(reference);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyActor {
|
||||
|
||||
public int getPid() {
|
||||
return SystemUtil.pid();
|
||||
}
|
||||
|
||||
public String hello() {
|
||||
return "hello";
|
||||
}
|
||||
}
|
||||
|
||||
private static String foo(ActorHandle<MyActor> myActor, ActorHandle<SignalActor> signal) {
|
||||
signal.task(SignalActor::waitSignal).remote().get();
|
||||
String result = myActor.task(MyActor::hello).remote().get();
|
||||
del(myActor);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void testActorHandleReferenceCount() {
|
||||
try {
|
||||
Ray.init();
|
||||
ActorHandle<SignalActor> signal = Ray.actor(SignalActor::new).remote();
|
||||
ActorHandle<MyActor> myActor = Ray.actor(MyActor::new).remote();
|
||||
int pid = myActor.task(MyActor::getPid).remote().get();
|
||||
// Pass the handle to another task that cannot run yet.
|
||||
ObjectRef<String> helloObj =
|
||||
Ray.task(ActorHandleReferenceCountTest::foo, myActor, signal).remote();
|
||||
// Delete the original handle. The actor should not get killed yet.
|
||||
del(myActor);
|
||||
// Once the task finishes, the actor process should get killed.
|
||||
signal.task(SignalActor::sendSignal).remote().get();
|
||||
Assert.assertEquals("hello", helloObj.get());
|
||||
Assert.assertTrue(TestUtils.waitForCondition(() -> !SystemUtil.isProcessAlive(pid), 10000));
|
||||
} finally {
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.options.ActorLifetime;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class ActorLifetimeTest {
|
||||
|
||||
private static class MyActor {
|
||||
public String echo(String str) {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
public void testDetached() throws IOException, InterruptedException {
|
||||
System.setProperty("ray.job.namespace", "test2");
|
||||
Ray.init();
|
||||
startDriver(DriverClassWithDetachedActor.class);
|
||||
Assert.assertTrue(Ray.getActor("my_actor").isPresent());
|
||||
Ray.shutdown();
|
||||
}
|
||||
|
||||
public void testNonDetached() throws IOException, InterruptedException {
|
||||
System.setProperty("ray.job.namespace", "test2");
|
||||
Ray.init();
|
||||
startDriver(DriverClassWithNonDetachedActor.class);
|
||||
Assert.assertFalse(Ray.getActor("my_actor").isPresent());
|
||||
Ray.shutdown();
|
||||
}
|
||||
|
||||
private static class DriverClassWithDetachedActor {
|
||||
public static void main(String[] argv) {
|
||||
System.setProperty("ray.job.long-running", "true");
|
||||
System.setProperty("ray.job.namespace", "test2");
|
||||
Ray.init();
|
||||
ActorHandle<MyActor> myActor =
|
||||
Ray.actor(MyActor::new).setLifetime(ActorLifetime.DETACHED).setName("my_actor").remote();
|
||||
myActor.task(MyActor::echo, "hello").remote().get();
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private static class DriverClassWithNonDetachedActor {
|
||||
public static void main(String[] argv) {
|
||||
System.setProperty("ray.job.namespace", "test2");
|
||||
Ray.init();
|
||||
ActorHandle<MyActor> myActor = Ray.actor(MyActor::new).setName("my_actor").remote();
|
||||
myActor.task(MyActor::echo, "hello").remote().get();
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private static void startDriver(Class<?> driverClass) throws IOException, InterruptedException {
|
||||
Process driver = null;
|
||||
try {
|
||||
ProcessBuilder builder = TestUtils.buildDriver(driverClass, null);
|
||||
builder.redirectError(ProcessBuilder.Redirect.INHERIT);
|
||||
driver = builder.start();
|
||||
// Wait for driver to start.
|
||||
driver.waitFor();
|
||||
System.out.println("Driver finished.");
|
||||
} finally {
|
||||
if (driver != null) {
|
||||
driver.waitFor(1, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.exception.RayActorException;
|
||||
import io.ray.api.exception.RayException;
|
||||
import io.ray.runtime.util.SystemUtil;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class ActorRestartTest extends BaseTest {
|
||||
|
||||
public static class Counter {
|
||||
|
||||
protected int value = 0;
|
||||
|
||||
private boolean wasCurrentActorRestarted = false;
|
||||
|
||||
public Counter() {
|
||||
wasCurrentActorRestarted = Ray.getRuntimeContext().wasCurrentActorRestarted();
|
||||
}
|
||||
|
||||
public boolean checkWasCurrentActorRestartedInActorCreationTask() {
|
||||
return wasCurrentActorRestarted;
|
||||
}
|
||||
|
||||
public int increase() {
|
||||
value += 1;
|
||||
return value;
|
||||
}
|
||||
|
||||
public int increaseAfterTimeout(int timeout) {
|
||||
try {
|
||||
Thread.sleep(timeout);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
value += 1;
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean checkWasCurrentActorRestartedInActorTask() {
|
||||
return Ray.getRuntimeContext().wasCurrentActorRestarted();
|
||||
}
|
||||
|
||||
public int getPid() {
|
||||
return SystemUtil.pid();
|
||||
}
|
||||
}
|
||||
|
||||
public void testActorRestart() throws InterruptedException, IOException {
|
||||
ActorHandle<Counter> actor = Ray.actor(Counter::new).setMaxRestarts(1).remote();
|
||||
// Call increase 3 times.
|
||||
for (int i = 0; i < 3; i++) {
|
||||
actor.task(Counter::increase).remote().get();
|
||||
}
|
||||
|
||||
// Check if actor was restarted.
|
||||
Assert.assertFalse(
|
||||
actor.task(Counter::checkWasCurrentActorRestartedInActorCreationTask).remote().get());
|
||||
Assert.assertFalse(
|
||||
actor.task(Counter::checkWasCurrentActorRestartedInActorTask).remote().get());
|
||||
|
||||
// Kill the actor process.
|
||||
int pid = actor.task(Counter::getPid).remote().get();
|
||||
killActorProcess(pid);
|
||||
|
||||
waitForActorAlive(actor);
|
||||
int value = actor.task(Counter::increase).remote().get();
|
||||
Assert.assertEquals(value, 1);
|
||||
|
||||
// Check if actor was restarted again.
|
||||
Assert.assertTrue(
|
||||
actor.task(Counter::checkWasCurrentActorRestartedInActorCreationTask).remote().get());
|
||||
Assert.assertTrue(actor.task(Counter::checkWasCurrentActorRestartedInActorTask).remote().get());
|
||||
|
||||
// Kill the actor process again.
|
||||
pid = actor.task(Counter::getPid).remote().get();
|
||||
killActorProcess(pid);
|
||||
|
||||
// Try calling increase on this actor again and this should fail.
|
||||
Assert.assertThrows(
|
||||
RayActorException.class, () -> actor.task(Counter::increase).remote().get());
|
||||
}
|
||||
|
||||
public void testActorRestartWithRetry() throws InterruptedException, IOException {
|
||||
ActorHandle<Counter> actor =
|
||||
Ray.actor(Counter::new).setMaxRestarts(1).setMaxTaskRetries(1).remote();
|
||||
// Call increase 3 times.
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int result = actor.task(Counter::increase).remote().get();
|
||||
Assert.assertEquals(result, i + 1);
|
||||
}
|
||||
// Need to call getPid before submitting the task to kill
|
||||
int pid = actor.task(Counter::getPid).remote().get();
|
||||
// Task to kill
|
||||
ObjectRef<Integer> ref = actor.task(Counter::increaseAfterTimeout, 3000).remote();
|
||||
// Kill the actor process.
|
||||
killActorProcess(pid);
|
||||
waitForActorAlive(actor);
|
||||
// The task should fail and retry, so result is 1
|
||||
int result = ref.get();
|
||||
Assert.assertEquals(result, 1);
|
||||
// Check that we can still call the actor
|
||||
result = actor.task(Counter::increase).remote().get();
|
||||
Assert.assertEquals(result, 2);
|
||||
// Kill the actor process again.
|
||||
pid = actor.task(Counter::getPid).remote().get();
|
||||
killActorProcess(pid);
|
||||
// Try calling increase on this actor again and this should fail.
|
||||
Assert.assertThrows(
|
||||
RayActorException.class, () -> actor.task(Counter::increase).remote().get());
|
||||
}
|
||||
|
||||
/** The helper to kill a counter actor. */
|
||||
private static void killActorProcess(int pid) throws IOException, InterruptedException {
|
||||
// Kill the actor process.
|
||||
Process p = Runtime.getRuntime().exec("kill -9 " + pid);
|
||||
// Wait for the actor to be killed.
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
}
|
||||
|
||||
private static void waitForActorAlive(ActorHandle<Counter> actor) {
|
||||
Assert.assertTrue(
|
||||
TestUtils.waitForCondition(
|
||||
() -> {
|
||||
try {
|
||||
actor.task(Counter::getPid).remote().get();
|
||||
return true;
|
||||
} catch (RayException e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
10000));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.PyActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.exception.UnreconstructableException;
|
||||
import io.ray.api.id.ActorId;
|
||||
import io.ray.api.id.UniqueId;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test
|
||||
public class ActorTest extends BaseTest {
|
||||
|
||||
public static class Counter {
|
||||
|
||||
private int value;
|
||||
|
||||
public Counter(int initValue) {
|
||||
this.value = initValue;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void increase(int delta) {
|
||||
value += delta;
|
||||
}
|
||||
|
||||
public int increaseAndGet(int delta) {
|
||||
value += delta;
|
||||
return value;
|
||||
}
|
||||
|
||||
public int accessLargeObject(TestUtils.LargeObject largeObject) {
|
||||
value += largeObject.data.length;
|
||||
return value;
|
||||
}
|
||||
|
||||
public TestUtils.LargeObject createLargeObject() {
|
||||
return new TestUtils.LargeObject();
|
||||
}
|
||||
}
|
||||
|
||||
public void testCreateAndCallActor() {
|
||||
// Test creating an actor from a constructor
|
||||
ActorHandle<Counter> actor = Ray.actor(Counter::new, 1).remote();
|
||||
Assert.assertNotEquals(actor.getId(), ActorId.NIL);
|
||||
// A java actor is not a python actor
|
||||
Assert.assertFalse(actor instanceof PyActorHandle);
|
||||
// Test calling an actor
|
||||
Assert.assertEquals(Integer.valueOf(1), actor.task(Counter::getValue).remote().get());
|
||||
actor.task(Counter::increase, 1).remote();
|
||||
Assert.assertEquals(Integer.valueOf(3), actor.task(Counter::increaseAndGet, 1).remote().get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getting an object twice from the local memory store.
|
||||
*
|
||||
* <p>Objects are stored in core worker's local memory. And it will be removed after the first
|
||||
* get. To enable getting it twice, we cache the object in `RayObjectImpl`.
|
||||
*/
|
||||
public void testGetObjectTwice() {
|
||||
ActorHandle<Counter> actor = Ray.actor(Counter::new, 1).remote();
|
||||
ObjectRef<Integer> result = actor.task(Counter::getValue).remote();
|
||||
Assert.assertEquals(result.get(), Integer.valueOf(1));
|
||||
Assert.assertEquals(result.get(), Integer.valueOf(1));
|
||||
Assert.assertEquals(Ray.get(result), Integer.valueOf(1));
|
||||
}
|
||||
|
||||
public void testCallActorWithLargeObject() {
|
||||
ActorHandle<Counter> actor = Ray.actor(Counter::new, 1).remote();
|
||||
TestUtils.LargeObject largeObject = new TestUtils.LargeObject();
|
||||
Assert.assertEquals(
|
||||
Integer.valueOf(largeObject.data.length + 1),
|
||||
actor.task(Counter::accessLargeObject, largeObject).remote().get());
|
||||
}
|
||||
|
||||
static Counter factory(int initValue) {
|
||||
return new Counter(initValue);
|
||||
}
|
||||
|
||||
public void testCreateActorFromFactory() {
|
||||
// Test creating an actor from a factory method
|
||||
ActorHandle<Counter> actor = Ray.actor(ActorTest::factory, 1).remote();
|
||||
Assert.assertNotEquals(actor.getId(), UniqueId.NIL);
|
||||
// Test calling an actor
|
||||
Assert.assertEquals(Integer.valueOf(1), actor.task(Counter::getValue).remote().get());
|
||||
}
|
||||
|
||||
static int testActorAsFirstParameter(ActorHandle<Counter> actor, int delta) {
|
||||
ObjectRef<Integer> res = actor.task(Counter::increaseAndGet, delta).remote();
|
||||
return res.get();
|
||||
}
|
||||
|
||||
static int testActorAsSecondParameter(int delta, ActorHandle<Counter> actor) {
|
||||
ObjectRef<Integer> res = actor.task(Counter::increaseAndGet, delta).remote();
|
||||
return res.get();
|
||||
}
|
||||
|
||||
static int testActorAsFieldOfParameter(List<ActorHandle<Counter>> actor, int delta) {
|
||||
ObjectRef<Integer> res = actor.get(0).task(Counter::increaseAndGet, delta).remote();
|
||||
return res.get();
|
||||
}
|
||||
|
||||
public void testPassActorAsParameter() {
|
||||
ActorHandle<Counter> actor = Ray.actor(Counter::new, 0).remote();
|
||||
Assert.assertEquals(
|
||||
Integer.valueOf(1),
|
||||
Ray.task(ActorTest::testActorAsFirstParameter, actor, 1).remote().get());
|
||||
Assert.assertEquals(
|
||||
Integer.valueOf(11),
|
||||
Ray.task(ActorTest::testActorAsSecondParameter, 10, actor).remote().get());
|
||||
Assert.assertEquals(
|
||||
Integer.valueOf(111),
|
||||
Ray.task(ActorTest::testActorAsFieldOfParameter, Collections.singletonList(actor), 100)
|
||||
.remote()
|
||||
.get());
|
||||
}
|
||||
|
||||
// This test case follows `test_internal_free` in `python/ray/tests/test_advanced.py`.
|
||||
@Test(groups = {"cluster"})
|
||||
public void testUnreconstructableActorObject() throws InterruptedException {
|
||||
ActorHandle<Counter> counter = Ray.actor(Counter::new, 100).remote();
|
||||
// Call an actor method.
|
||||
ObjectRef value = counter.task(Counter::getValue).remote();
|
||||
Assert.assertEquals(100, value.get());
|
||||
// Delete the object from the object store.
|
||||
Ray.internal().free(ImmutableList.of(value), false);
|
||||
// Wait for delete RPC to propagate
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
// Free deletes from in-memory store.
|
||||
Assert.expectThrows(UnreconstructableException.class, () -> value.get());
|
||||
|
||||
// Call an actor method.
|
||||
ObjectRef<TestUtils.LargeObject> largeValue = counter.task(Counter::createLargeObject).remote();
|
||||
Assert.assertTrue(largeValue.get() instanceof TestUtils.LargeObject);
|
||||
// Delete the object from the object store.
|
||||
Ray.internal().free(ImmutableList.of(largeValue), false);
|
||||
// Wait for delete RPC to propagate
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
// Free deletes big objects from plasma store.
|
||||
Assert.expectThrows(UnreconstructableException.class, () -> largeValue.get());
|
||||
}
|
||||
|
||||
public interface ChildClassInterface {
|
||||
|
||||
default String interfaceName() {
|
||||
return ChildClassInterface.class.getName();
|
||||
}
|
||||
}
|
||||
|
||||
public static class ChildClass extends Counter implements ChildClassInterface {
|
||||
|
||||
public ChildClass(int initValue) {
|
||||
super(initValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void increase(int delta) {
|
||||
super.increase(-delta);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public void testInheritance() {
|
||||
ActorHandle<ChildClass> counter = Ray.actor(ChildClass::new, 100).remote();
|
||||
counter.task(ChildClass::increase, 10).remote();
|
||||
Assert.assertEquals(counter.task(ChildClass::getValue).remote().get(), Integer.valueOf(90));
|
||||
// Since `increase` method is overrided, call by super class method reference should still
|
||||
// execute child class methods.
|
||||
counter.task(Counter::increase, 10).remote();
|
||||
Assert.assertEquals(counter.task(Counter::getValue).remote().get(), Integer.valueOf(80));
|
||||
// test interface default methods
|
||||
Assert.assertEquals(
|
||||
counter.task(ChildClassInterface::interfaceName).remote().get(),
|
||||
ChildClassInterface.class.getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package io.ray.test;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import org.testng.IAnnotationTransformer;
|
||||
import org.testng.annotations.ITestAnnotation;
|
||||
|
||||
public class AnnotationTransformer implements IAnnotationTransformer {
|
||||
|
||||
@Override
|
||||
public void transform(
|
||||
ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
|
||||
annotation.setRetryAnalyzer(RetryAnalyzer.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.exception.PendingCallsLimitExceededException;
|
||||
import io.ray.api.id.ObjectId;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class BackPressureTest extends BaseTest {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(BackPressureTest.class);
|
||||
|
||||
@BeforeClass
|
||||
public void setupJobConfig() {}
|
||||
|
||||
private static final ObjectId objectId = ObjectId.fromRandom();
|
||||
|
||||
public static String unblockSignalActor(ActorHandle<SignalActor> signal) {
|
||||
signal.task(SignalActor::sendSignal).remote().get();
|
||||
return null;
|
||||
}
|
||||
|
||||
public void testBackPressure() {
|
||||
/// set max concurrency to 11, 10 of them for executing waitSignal, and 1
|
||||
/// of them for executing sendSignal.
|
||||
ActorHandle<SignalActor> signalActor =
|
||||
Ray.actor(SignalActor::new).setMaxConcurrency(11).setMaxPendingCalls(10).remote();
|
||||
/// Ping the actor to insure the actor is alive already.
|
||||
signalActor.task(SignalActor::ping).remote().get();
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
LOGGER.info("call waitSignal");
|
||||
Assert.assertNotNull(signalActor.task(SignalActor::waitSignal).remote());
|
||||
}
|
||||
|
||||
// Check backpressure occur.
|
||||
boolean backPressure = false;
|
||||
try {
|
||||
LOGGER.info("call waitSignal");
|
||||
signalActor.task(SignalActor::waitSignal).remote();
|
||||
} catch (PendingCallsLimitExceededException e) {
|
||||
LOGGER.info(e.toString());
|
||||
backPressure = true;
|
||||
} finally {
|
||||
Assert.assertTrue(backPressure);
|
||||
}
|
||||
|
||||
// Unblock signal actor, to make all backpressured raycall executed.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Ray.task(BackPressureTest::unblockSignalActor, signalActor).remote().get();
|
||||
}
|
||||
|
||||
// Check the raycall is normal
|
||||
signalActor.task(SignalActor::ping).remote().get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package io.ray.test;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.ray.api.options.BaseTaskOptions;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class BaseTaskOptionsTest {
|
||||
|
||||
public static class MockActorCreationOptions extends BaseTaskOptions {
|
||||
public MockActorCreationOptions(Map<String, Double> resources) {
|
||||
super(resources);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLegalResources() {
|
||||
double precision = 0.001;
|
||||
Map<String, Double> inputResources =
|
||||
ImmutableMap.of("CPU", 0.5, "GPU", 3.0, "memory", 1024.0, "A", 4294967296.0);
|
||||
Map<String, Double> resources = new MockActorCreationOptions(inputResources).getResources();
|
||||
|
||||
assertEquals(resources.get("CPU"), 0.5, precision);
|
||||
assertEquals(resources.get("GPU"), 3.0, precision);
|
||||
assertEquals(resources.get("memory"), 1024.0, precision);
|
||||
assertEquals(resources.get("A"), 4294967296.0, precision);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourcesFiltering() {
|
||||
Map<String, Double> inputResources = ImmutableMap.of("CPU", 0.0, "GPU", 0.0);
|
||||
Map<String, Double> resources = new MockActorCreationOptions(inputResources).getResources();
|
||||
|
||||
assertTrue(resources.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyResourceMap() {
|
||||
Map<String, Double> resources = new HashMap<>();
|
||||
MockActorCreationOptions options = new MockActorCreationOptions(resources);
|
||||
assertTrue(options.getResources().isEmpty());
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = {IllegalArgumentException.class})
|
||||
public void testNullResourceMap() {
|
||||
new MockActorCreationOptions(null);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = {IllegalArgumentException.class})
|
||||
public void testNullResourceKey() {
|
||||
Map<String, Double> resources = new HashMap<>();
|
||||
resources.put(null, 1.0);
|
||||
new MockActorCreationOptions(resources);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = {UnsupportedOperationException.class})
|
||||
public void testResourcesImmutability() {
|
||||
Map<String, Double> inputResources = new HashMap<>();
|
||||
inputResources.put("CPU", 2.0);
|
||||
|
||||
MockActorCreationOptions options = new MockActorCreationOptions(inputResources);
|
||||
Map<String, Double> resources = options.getResources();
|
||||
resources.put("GPU", 1.0);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = {IllegalArgumentException.class})
|
||||
public void testIllegalResourcesWithNullValue() {
|
||||
Map<String, Double> resources = new HashMap<>();
|
||||
resources.put("CPU", null);
|
||||
new MockActorCreationOptions(resources);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIllegalResourcesWithZeroValue() {
|
||||
Map<String, Double> resources = ImmutableMap.of("CPU", 0.0);
|
||||
new MockActorCreationOptions(resources);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = {IllegalArgumentException.class})
|
||||
public void testIllegalResourcesWithNegativeValue() {
|
||||
Map<String, Double> resources = ImmutableMap.of("CPU", -1.0);
|
||||
new MockActorCreationOptions(resources);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = {IllegalArgumentException.class})
|
||||
public void testIllegalResourcesWithNonIntegerValue() {
|
||||
Map<String, Double> resources = ImmutableMap.of("CPU", 3.5);
|
||||
new MockActorCreationOptions(resources);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.Ray;
|
||||
import java.lang.reflect.Method;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.AfterMethod;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
|
||||
public class BaseTest {
|
||||
|
||||
@BeforeMethod(alwaysRun = true)
|
||||
public void setUpBase(Method method) {
|
||||
Assert.assertFalse(Ray.isInitialized());
|
||||
Ray.init();
|
||||
}
|
||||
|
||||
@AfterMethod(alwaysRun = true)
|
||||
public void tearDownBase() {
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.concurrencygroup.ConcurrencyGroup;
|
||||
import io.ray.api.concurrencygroup.ConcurrencyGroupBuilder;
|
||||
import io.ray.api.concurrencygroup.annotations.DefConcurrencyGroup;
|
||||
import io.ray.api.concurrencygroup.annotations.UseConcurrencyGroup;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test
|
||||
public class ConcurrencyGroupTest extends BaseTest {
|
||||
|
||||
private static class ConcurrentActor {
|
||||
public long f1() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
public long f2() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
public long f3(int a, int b) {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
public long f4() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
public long f5() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
public long f6() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
}
|
||||
|
||||
public void testLimitMethodsInOneGroup() {
|
||||
ConcurrencyGroup group1 =
|
||||
new ConcurrencyGroupBuilder<ConcurrentActor>()
|
||||
.setName("io")
|
||||
.setMaxConcurrency(1)
|
||||
.addMethod(ConcurrentActor::f1)
|
||||
.addMethod(ConcurrentActor::f2)
|
||||
.build();
|
||||
|
||||
ConcurrencyGroup group2 =
|
||||
new ConcurrencyGroupBuilder<ConcurrentActor>()
|
||||
.setName("executing")
|
||||
.setMaxConcurrency(1)
|
||||
.addMethod(ConcurrentActor::f3)
|
||||
.addMethod(ConcurrentActor::f4)
|
||||
.build();
|
||||
|
||||
ActorHandle<ConcurrentActor> myActor =
|
||||
Ray.actor(ConcurrentActor::new).setConcurrencyGroups(group1, group2).remote();
|
||||
|
||||
long threadId1 = myActor.task(ConcurrentActor::f1).remote().get();
|
||||
long threadId2 = myActor.task(ConcurrentActor::f2).remote().get();
|
||||
long threadId3 = myActor.task(ConcurrentActor::f3, 3, 5).remote().get();
|
||||
long threadId4 = myActor.task(ConcurrentActor::f4).remote().get();
|
||||
long threadId5 = myActor.task(ConcurrentActor::f5).remote().get();
|
||||
long threadId6 = myActor.task(ConcurrentActor::f6).remote().get();
|
||||
long threadId7 =
|
||||
myActor.task(ConcurrentActor::f1).setConcurrencyGroup("executing").remote().get();
|
||||
|
||||
Assert.assertEquals(threadId1, threadId2);
|
||||
Assert.assertEquals(threadId3, threadId4);
|
||||
Assert.assertEquals(threadId5, threadId6);
|
||||
Assert.assertNotEquals(threadId1, threadId3);
|
||||
Assert.assertNotEquals(threadId1, threadId5);
|
||||
Assert.assertNotEquals(threadId3, threadId5);
|
||||
Assert.assertEquals(threadId3, threadId7);
|
||||
}
|
||||
|
||||
private static class CountDownActor {
|
||||
// The countdown for f1, f2.
|
||||
private final CountDownLatch countDownLatch1 = new CountDownLatch(2);
|
||||
|
||||
// The countdown for f3, f4.
|
||||
private final CountDownLatch countDownLatch2 = new CountDownLatch(4);
|
||||
|
||||
// The countdown for default method f5.
|
||||
private final CountDownLatch countDownLatch3 = new CountDownLatch(2);
|
||||
|
||||
private static boolean countDown(CountDownLatch countDownLatch) {
|
||||
countDownLatch.countDown();
|
||||
try {
|
||||
countDownLatch.await();
|
||||
return true;
|
||||
} catch (InterruptedException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean f1(double a) {
|
||||
return countDown(countDownLatch1);
|
||||
}
|
||||
|
||||
public boolean f2(int a, double b) {
|
||||
return countDown(countDownLatch1);
|
||||
}
|
||||
|
||||
public boolean f3(int a, int b, double c) {
|
||||
return countDown(countDownLatch2);
|
||||
}
|
||||
|
||||
public boolean f4(double a, int b, double c, int d) {
|
||||
return countDown(countDownLatch2);
|
||||
}
|
||||
|
||||
public boolean f5() {
|
||||
return countDown(countDownLatch3);
|
||||
}
|
||||
}
|
||||
|
||||
public void testMaxConcurrencyForGroups() {
|
||||
ConcurrencyGroup group1 =
|
||||
new ConcurrencyGroupBuilder<CountDownActor>()
|
||||
.setName("io")
|
||||
.setMaxConcurrency(4)
|
||||
.addMethod(CountDownActor::f1)
|
||||
.addMethod(CountDownActor::f2)
|
||||
.build();
|
||||
|
||||
ConcurrencyGroup group2 =
|
||||
new ConcurrencyGroupBuilder<CountDownActor>()
|
||||
.setName("executing")
|
||||
.setMaxConcurrency(4)
|
||||
.addMethod(CountDownActor::f3)
|
||||
.addMethod(CountDownActor::f4)
|
||||
.build();
|
||||
|
||||
ActorHandle<CountDownActor> myActor =
|
||||
Ray.actor(CountDownActor::new)
|
||||
.setConcurrencyGroups(group1, group2)
|
||||
.setMaxConcurrency(3)
|
||||
.remote();
|
||||
|
||||
ObjectRef<Boolean> ret1 = myActor.task(CountDownActor::f1, 2.0).remote();
|
||||
ObjectRef<Boolean> ret2 = myActor.task(CountDownActor::f2, 1, 2.0).remote();
|
||||
Assert.assertTrue(ret1.get());
|
||||
Assert.assertTrue(ret2.get());
|
||||
|
||||
ObjectRef<Boolean> ret3 = myActor.task(CountDownActor::f3, 3, 5, 2.0).remote();
|
||||
ObjectRef<Boolean> ret4 = myActor.task(CountDownActor::f4, 1.0, 2, 3.0, 4).remote();
|
||||
ObjectRef<Boolean> ret5 = myActor.task(CountDownActor::f3, 3, 5, 2.0).remote();
|
||||
ObjectRef<Boolean> ret6 = myActor.task(CountDownActor::f4, 1.0, 2, 3.0, 4).remote();
|
||||
Assert.assertTrue(ret3.get());
|
||||
Assert.assertTrue(ret4.get());
|
||||
Assert.assertTrue(ret5.get());
|
||||
Assert.assertTrue(ret6.get());
|
||||
|
||||
ObjectRef<Boolean> ret7 = myActor.task(CountDownActor::f5).remote();
|
||||
ObjectRef<Boolean> ret8 = myActor.task(CountDownActor::f5).remote();
|
||||
Assert.assertTrue(ret7.get());
|
||||
Assert.assertTrue(ret8.get());
|
||||
}
|
||||
|
||||
private static class ConcurrencyActor2 {
|
||||
|
||||
public String f1() throws InterruptedException {
|
||||
TimeUnit.MINUTES.sleep(100);
|
||||
return "never returned";
|
||||
}
|
||||
|
||||
public String f2() {
|
||||
return "ok";
|
||||
}
|
||||
}
|
||||
|
||||
/// This case tests that blocking task in default group will block other groups.
|
||||
/// See https://github.com/ray-project/ray/issues/20475
|
||||
@Test(groups = {"cluster"})
|
||||
public void testDefaultCgDoNotBlockOthers() {
|
||||
ConcurrencyGroup group =
|
||||
new ConcurrencyGroupBuilder<ConcurrencyActor2>()
|
||||
.setName("group")
|
||||
.setMaxConcurrency(1)
|
||||
.addMethod(ConcurrencyActor2::f2)
|
||||
.build();
|
||||
|
||||
ActorHandle<ConcurrencyActor2> myActor =
|
||||
Ray.actor(ConcurrencyActor2::new).setConcurrencyGroups(group).remote();
|
||||
myActor.task(ConcurrencyActor2::f1).remote();
|
||||
Assert.assertEquals(myActor.task(ConcurrencyActor2::f2).remote().get(), "ok");
|
||||
}
|
||||
|
||||
/// This case tests that the blocking concurrency group doesn't block the scheduling of
|
||||
/// other concurrency groups. See https://github.com/ray-project/ray/issues/19593 for details.
|
||||
@Test(groups = {"cluster"})
|
||||
public void testBlockingCgNotBlockOthers() {
|
||||
ConcurrencyGroup group1 =
|
||||
new ConcurrencyGroupBuilder<ConcurrencyActor2>()
|
||||
.setName("group1")
|
||||
.setMaxConcurrency(1)
|
||||
.addMethod(ConcurrencyActor2::f1)
|
||||
.build();
|
||||
|
||||
ConcurrencyGroup group2 =
|
||||
new ConcurrencyGroupBuilder<ConcurrencyActor2>()
|
||||
.setName("group2")
|
||||
.setMaxConcurrency(1)
|
||||
.addMethod(ConcurrencyActor2::f2)
|
||||
.build();
|
||||
|
||||
ActorHandle<ConcurrencyActor2> myActor =
|
||||
Ray.actor(ConcurrencyActor2::new).setConcurrencyGroups(group1, group2).remote();
|
||||
|
||||
// Execute f1 twice. and the cg1 is blocking, but cg2 should work well.
|
||||
ObjectRef<String> obj0 = myActor.task(ConcurrencyActor2::f1).remote();
|
||||
ObjectRef<String> obj1 = myActor.task(ConcurrencyActor2::f1).remote();
|
||||
// Wait a while to make sure f2 is scheduled after f1.
|
||||
Ray.wait(ImmutableList.of(obj0, obj1), 2, 5 * 1000);
|
||||
|
||||
// f2 should work well even if group1 is blocking.
|
||||
Assert.assertEquals(myActor.task(ConcurrencyActor2::f2).remote().get(), "ok");
|
||||
}
|
||||
|
||||
@DefConcurrencyGroup(name = "io", maxConcurrency = 1)
|
||||
@DefConcurrencyGroup(name = "compute", maxConcurrency = 1)
|
||||
private static class StaticDefinedConcurrentActor {
|
||||
@UseConcurrencyGroup(name = "io")
|
||||
public long f1() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
@UseConcurrencyGroup(name = "io")
|
||||
public long f2() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
@UseConcurrencyGroup(name = "compute")
|
||||
public long f3(int a, int b) {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
@UseConcurrencyGroup(name = "compute")
|
||||
public long f4() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
public long f5() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
public long f6() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ChildActor extends StaticDefinedConcurrentActor {}
|
||||
|
||||
public void testLimitMethodsInOneGroupOfStaticDefinition() {
|
||||
ActorHandle<ChildActor> myActor = Ray.actor(ChildActor::new).remote();
|
||||
long threadId1 = myActor.task(StaticDefinedConcurrentActor::f1).remote().get();
|
||||
long threadId2 = myActor.task(StaticDefinedConcurrentActor::f2).remote().get();
|
||||
long threadId3 = myActor.task(StaticDefinedConcurrentActor::f3, 3, 5).remote().get();
|
||||
long threadId4 = myActor.task(StaticDefinedConcurrentActor::f4).remote().get();
|
||||
long threadId5 = myActor.task(StaticDefinedConcurrentActor::f5).remote().get();
|
||||
long threadId6 = myActor.task(StaticDefinedConcurrentActor::f6).remote().get();
|
||||
Assert.assertEquals(threadId1, threadId2);
|
||||
Assert.assertEquals(threadId3, threadId4);
|
||||
Assert.assertEquals(threadId5, threadId6);
|
||||
Assert.assertNotEquals(threadId1, threadId3);
|
||||
Assert.assertNotEquals(threadId1, threadId5);
|
||||
Assert.assertNotEquals(threadId3, threadId5);
|
||||
}
|
||||
|
||||
@DefConcurrencyGroup(name = "io", maxConcurrency = 1)
|
||||
@DefConcurrencyGroup(name = "compute", maxConcurrency = 1)
|
||||
private interface StaticDefinedInterface {
|
||||
|
||||
@UseConcurrencyGroup(name = "io")
|
||||
public default long f1() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
@UseConcurrencyGroup(name = "io")
|
||||
public default long f2() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
@UseConcurrencyGroup(name = "compute")
|
||||
public default long f3(int a, int b) {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
@UseConcurrencyGroup(name = "compute")
|
||||
public default long f4() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
public default long f5() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
public default long f6() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ChildActor2 implements StaticDefinedInterface {}
|
||||
|
||||
public void testLimitMethodsInOneGroupOfStaticDefinitionForInterface() {
|
||||
ActorHandle<ChildActor2> myActor = Ray.actor(ChildActor2::new).remote();
|
||||
|
||||
long threadId1 = myActor.task(StaticDefinedInterface::f1).remote().get();
|
||||
long threadId2 = myActor.task(StaticDefinedInterface::f2).remote().get();
|
||||
long threadId3 = myActor.task(StaticDefinedInterface::f3, 3, 5).remote().get();
|
||||
long threadId4 = myActor.task(StaticDefinedInterface::f4).remote().get();
|
||||
long threadId5 = myActor.task(StaticDefinedInterface::f5).remote().get();
|
||||
long threadId6 = myActor.task(StaticDefinedInterface::f6).remote().get();
|
||||
|
||||
Assert.assertEquals(threadId1, threadId2);
|
||||
Assert.assertEquals(threadId3, threadId4);
|
||||
Assert.assertEquals(threadId5, threadId6);
|
||||
Assert.assertNotEquals(threadId1, threadId3);
|
||||
Assert.assertNotEquals(threadId1, threadId5);
|
||||
Assert.assertNotEquals(threadId3, threadId5);
|
||||
}
|
||||
|
||||
@DefConcurrencyGroup(name = "io", maxConcurrency = 1)
|
||||
private static class SingleGroupConcurrentActor {
|
||||
|
||||
@UseConcurrencyGroup(name = "io")
|
||||
public long f1() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
@UseConcurrencyGroup(name = "io")
|
||||
public long f2() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
public long f3(int a, int b) {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
public long f4() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
public long f5() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
|
||||
public long f6() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ChildActor3 extends SingleGroupConcurrentActor {}
|
||||
|
||||
public void testSingleConcurrencyActor() {
|
||||
ActorHandle<ChildActor3> myActor = Ray.actor(ChildActor3::new).remote();
|
||||
|
||||
long threadId1 = myActor.task(SingleGroupConcurrentActor::f1).remote().get();
|
||||
long threadId2 = myActor.task(SingleGroupConcurrentActor::f2).remote().get();
|
||||
long threadId3 = myActor.task(SingleGroupConcurrentActor::f3, 3, 5).remote().get();
|
||||
long threadId4 = myActor.task(SingleGroupConcurrentActor::f4).remote().get();
|
||||
long threadId5 = myActor.task(SingleGroupConcurrentActor::f5).remote().get();
|
||||
long threadId6 = myActor.task(SingleGroupConcurrentActor::f6).remote().get();
|
||||
|
||||
Assert.assertEquals(threadId1, threadId2);
|
||||
Assert.assertEquals(threadId3, threadId4);
|
||||
Assert.assertEquals(threadId5, threadId6);
|
||||
Assert.assertNotEquals(threadId1, threadId3);
|
||||
Assert.assertNotEquals(threadId1, threadId5);
|
||||
Assert.assertEquals(threadId3, threadId5);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import org.testng.Assert;
|
||||
|
||||
public class Counter {
|
||||
|
||||
private int value;
|
||||
|
||||
private ActorHandle<Counter> childActor;
|
||||
|
||||
public Counter(int initValue) {
|
||||
this.value = initValue;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void increase(int delta) {
|
||||
value += delta;
|
||||
}
|
||||
|
||||
public int increaseAndGet(int delta) {
|
||||
value += delta;
|
||||
return value;
|
||||
}
|
||||
|
||||
public String echo(String str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
public byte[] echoBytes(byte[] bytes) {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public String createChildActor(String actorName) {
|
||||
childActor = Ray.actor(Counter::new, 0).setName(actorName).remote();
|
||||
Assert.assertEquals(Integer.valueOf(0), childActor.task(Counter::getValue).remote().get());
|
||||
return "OK";
|
||||
}
|
||||
|
||||
public static class NestedActor {
|
||||
|
||||
public NestedActor(String s) {
|
||||
str = s;
|
||||
}
|
||||
|
||||
public String concat(String s) {
|
||||
return str + s;
|
||||
}
|
||||
|
||||
private String str;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,590 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.CppActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.PyActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.exception.CrossLanguageException;
|
||||
import io.ray.api.exception.RayException;
|
||||
import io.ray.api.exception.RayTimeoutException;
|
||||
import io.ray.api.function.CppActorClass;
|
||||
import io.ray.api.function.CppActorMethod;
|
||||
import io.ray.api.function.CppFunction;
|
||||
import io.ray.api.function.PyActorClass;
|
||||
import io.ray.api.function.PyActorMethod;
|
||||
import io.ray.api.function.PyFunction;
|
||||
import io.ray.runtime.actor.NativeActorHandle;
|
||||
import io.ray.runtime.serializer.RayExceptionSerializer;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class CrossLanguageInvocationTest extends BaseTest {
|
||||
|
||||
private static final String PYTHON_MODULE = "test_cross_language_invocation";
|
||||
private static final String[] CPP_LIBRARYS = {"counter", "plus"};
|
||||
|
||||
@BeforeClass
|
||||
public void beforeClass() {
|
||||
// Delete and re-create the temp dir.
|
||||
File tempDir =
|
||||
new File(System.getProperty("java.io.tmpdir") + File.separator + "ray_cross_language_test");
|
||||
FileUtils.deleteQuietly(tempDir);
|
||||
tempDir.mkdirs();
|
||||
tempDir.deleteOnExit();
|
||||
|
||||
// Write the test Python file to the temp dir.
|
||||
InputStream in =
|
||||
CrossLanguageInvocationTest.class.getResourceAsStream(
|
||||
File.separator + PYTHON_MODULE + ".py");
|
||||
File pythonFile = new File(tempDir.getAbsolutePath() + File.separator + PYTHON_MODULE + ".py");
|
||||
try {
|
||||
FileUtils.copyInputStreamToFile(in, pythonFile);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
// Write the test Cpp files to the temp dir.
|
||||
for (String lib : CPP_LIBRARYS) {
|
||||
in =
|
||||
CrossLanguageInvocationTest.class.getResourceAsStream(
|
||||
File.separator + "cpp" + File.separator + lib + ".so");
|
||||
File cppFile = new File(tempDir.getAbsolutePath() + File.separator + lib + ".so");
|
||||
try {
|
||||
FileUtils.copyInputStreamToFile(in, cppFile);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
System.setProperty(
|
||||
"ray.job.code-search-path",
|
||||
System.getProperty("java.class.path") + File.pathSeparator + tempDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallingPythonFunction() {
|
||||
Object[] inputs =
|
||||
new Object[] {
|
||||
true, // Boolean
|
||||
Byte.MAX_VALUE, // Byte
|
||||
Short.MAX_VALUE, // Short
|
||||
Integer.MAX_VALUE, // Integer
|
||||
Long.MAX_VALUE, // Long
|
||||
// BigInteger can support max value of 2^64-1, please refer to:
|
||||
// https://github.com/msgpack/msgpack/blob/master/spec.md#int-format-family
|
||||
// If BigInteger larger than 2^64-1, the value can only be transferred among Java workers.
|
||||
BigInteger.valueOf(Long.MAX_VALUE), // BigInteger
|
||||
"Hello World!", // String
|
||||
1.234f, // Float
|
||||
1.234, // Double
|
||||
"example binary".getBytes()
|
||||
}; // byte[]
|
||||
for (Object o : inputs) {
|
||||
ObjectRef res =
|
||||
Ray.task(PyFunction.of(PYTHON_MODULE, "py_return_input", o.getClass()), o).remote();
|
||||
Assert.assertEquals(res.get(), o);
|
||||
}
|
||||
// null
|
||||
{
|
||||
Object input = null;
|
||||
ObjectRef<Object> res =
|
||||
Ray.task(PyFunction.of(PYTHON_MODULE, "py_return_input", Object.class), input).remote();
|
||||
Object r = res.get();
|
||||
Assert.assertEquals(r, input);
|
||||
}
|
||||
// array
|
||||
{
|
||||
int[] input = new int[] {1, 2};
|
||||
ObjectRef<int[]> res =
|
||||
Ray.task(PyFunction.of(PYTHON_MODULE, "py_return_input", int[].class), input).remote();
|
||||
int[] r = res.get();
|
||||
Assert.assertEquals(r, input);
|
||||
}
|
||||
// array of Object
|
||||
{
|
||||
Object[] input =
|
||||
new Object[] {1, 2.3f, 4.56, "789", "10".getBytes(), null, true, new int[] {1, 2}};
|
||||
ObjectRef<Object[]> res =
|
||||
Ray.task(PyFunction.of(PYTHON_MODULE, "py_return_input", Object[].class), input).remote();
|
||||
Object[] r = res.get();
|
||||
// If we tell the value type is Object, then all numbers will be Number type.
|
||||
Assert.assertEquals(((Number) r[0]).intValue(), input[0]);
|
||||
Assert.assertEquals(((Number) r[1]).floatValue(), input[1]);
|
||||
Assert.assertEquals(((Number) r[2]).doubleValue(), input[2]);
|
||||
// String cast
|
||||
Assert.assertEquals((String) r[3], input[3]);
|
||||
// binary cast
|
||||
Assert.assertEquals((byte[]) r[4], input[4]);
|
||||
// null
|
||||
Assert.assertEquals(r[5], input[5]);
|
||||
// Boolean cast
|
||||
Assert.assertEquals((Boolean) r[6], input[6]);
|
||||
// array cast
|
||||
Object[] r7array = (Object[]) r[7];
|
||||
int[] input7array = (int[]) input[7];
|
||||
Assert.assertEquals(((Number) r7array[0]).intValue(), input7array[0]);
|
||||
Assert.assertEquals(((Number) r7array[1]).intValue(), input7array[1]);
|
||||
}
|
||||
// objectRef
|
||||
{
|
||||
ObjectRef<Integer> input = Ray.put(1);
|
||||
ObjectRef<Integer> res =
|
||||
Ray.task(PyFunction.of(PYTHON_MODULE, "py_return_input", Integer.class), input).remote();
|
||||
Assert.assertEquals(res.get(), input.get());
|
||||
}
|
||||
// Unsupported types, all Java specific types, e.g. List / Map...
|
||||
{
|
||||
Assert.expectThrows(
|
||||
Exception.class,
|
||||
() -> {
|
||||
List<Integer> input = Arrays.asList(1, 2);
|
||||
ObjectRef<List<Integer>> res =
|
||||
Ray.task(
|
||||
PyFunction.of(
|
||||
PYTHON_MODULE,
|
||||
"py_return_input",
|
||||
(Class<List<Integer>>) input.getClass()),
|
||||
input)
|
||||
.remote();
|
||||
List<Integer> r = res.get();
|
||||
Assert.assertEquals(r, input);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPythonCallJavaFunction() {
|
||||
ObjectRef<String> res =
|
||||
Ray.task(PyFunction.of(PYTHON_MODULE, "py_func_call_java_function", String.class)).remote();
|
||||
Assert.assertEquals(res.get(), "success");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallingPythonActor() {
|
||||
PyActorHandle actor =
|
||||
Ray.actor(PyActorClass.of(PYTHON_MODULE, "Counter"), "1".getBytes()).remote();
|
||||
ObjectRef<byte[]> res =
|
||||
actor.task(PyActorMethod.of("increase", byte[].class), "1".getBytes()).remote();
|
||||
Assert.assertEquals(res.get(), "2".getBytes());
|
||||
|
||||
ObjectRef<String> numRef = Ray.put("2");
|
||||
ObjectRef<byte[]> res2 =
|
||||
actor.task(PyActorMethod.of("increase", byte[].class), numRef).remote();
|
||||
Assert.assertEquals(res2.get(), "4".getBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallingPythonAsyncActor() {
|
||||
{
|
||||
PyActorHandle actor =
|
||||
Ray.actor(PyActorClass.of(PYTHON_MODULE, "AsyncCounter"), "1".getBytes())
|
||||
.setAsync(true)
|
||||
.remote();
|
||||
actor.task(PyActorMethod.of("block_task", byte[].class)).remote();
|
||||
ObjectRef<byte[]> res =
|
||||
actor.task(PyActorMethod.of("increase", byte[].class), "1".getBytes()).remote();
|
||||
Assert.assertEquals(res.get(), "2".getBytes());
|
||||
}
|
||||
{
|
||||
PyActorHandle actor =
|
||||
Ray.actor(PyActorClass.of(PYTHON_MODULE, "SyncCounter"), "1".getBytes())
|
||||
.setAsync(false)
|
||||
.remote();
|
||||
actor.task(PyActorMethod.of("block_task", byte[].class)).remote();
|
||||
ObjectRef<byte[]> res =
|
||||
actor.task(PyActorMethod.of("increase", byte[].class), "1".getBytes()).remote();
|
||||
Supplier<Boolean> getValue =
|
||||
() -> {
|
||||
if (equals(res.get() == "2".getBytes())) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
Assert.assertFalse(TestUtils.waitForCondition(getValue, 30000));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallingCppFunction() {
|
||||
// Test calling a simple C++ function.
|
||||
ObjectRef<Integer> res = Ray.task(CppFunction.of("Plus", Integer.class), 1, 2).remote();
|
||||
Assert.assertEquals(res.get(), Integer.valueOf(3));
|
||||
// Test calling a C++ function that returns a large object.
|
||||
ObjectRef<int[]> res1 =
|
||||
Ray.task(CppFunction.of("ReturnLargeArray", int[].class), new int[100000]).remote();
|
||||
Assert.assertEquals(res1.get().length, 100000);
|
||||
// Test calling a C++ function with String type input/output.
|
||||
ObjectRef<String> res2 = Ray.task(CppFunction.of("Echo", String.class), "CallCpp").remote();
|
||||
Assert.assertEquals(res2.get(), "CallCpp");
|
||||
// Test calling a C++ function that throws an exception.
|
||||
Assert.expectThrows(
|
||||
Exception.class,
|
||||
() -> {
|
||||
ObjectRef<Object> res3 = Ray.task(CppFunction.of("ThrowTask")).remote();
|
||||
res3.get();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallingCppActor() {
|
||||
String actorName = "actor_name";
|
||||
CppActorHandle actor =
|
||||
Ray.actor(CppActorClass.of("RAY_FUNC(Counter::FactoryCreate)", "Counter"))
|
||||
.setName(actorName)
|
||||
.remote();
|
||||
ObjectRef<Integer> res = actor.task(CppActorMethod.of("Plus1", Integer.class)).remote();
|
||||
Assert.assertEquals(res.get(), Integer.valueOf(1));
|
||||
ObjectRef<byte[]> b =
|
||||
actor.task(CppActorMethod.of("GetBytes", byte[].class), "C++ Worker").remote();
|
||||
Assert.assertEquals(b.get(), "C++ Worker".getBytes());
|
||||
|
||||
ObjectRef<byte[]> b2 =
|
||||
actor.task(CppActorMethod.of("EchoBytes", byte[].class), "C++ Worker".getBytes()).remote();
|
||||
Assert.assertEquals(b2.get(), "C++ Worker".getBytes());
|
||||
|
||||
ObjectRef<byte[]> b3 =
|
||||
actor.task(CppActorMethod.of("EchoBytes", byte[].class), new byte[0]).remote();
|
||||
Assert.assertEquals(b3.get(), new byte[0]);
|
||||
|
||||
ObjectRef<byte[]> b4 = actor.task(CppActorMethod.of("EchoBytes", byte[].class), null).remote();
|
||||
Assert.assertThrows(CrossLanguageException.class, () -> b4.get());
|
||||
|
||||
// Test get cpp actor by actor name.
|
||||
Optional<CppActorHandle> optional = Ray.getActor(actorName);
|
||||
Assert.assertTrue(optional.isPresent());
|
||||
CppActorHandle actor2 = optional.get();
|
||||
ObjectRef<Integer> res2 = actor2.task(CppActorMethod.of("Plus1", Integer.class)).remote();
|
||||
Assert.assertEquals(res2.get(), Integer.valueOf(2));
|
||||
|
||||
// Test get other cpp actor by actor name.
|
||||
String childName = "child_name";
|
||||
ObjectRef<String> res3 =
|
||||
actor.task(CppActorMethod.of("CreateNestedChildActor", String.class), childName).remote();
|
||||
Assert.assertEquals(res3.get(), "OK");
|
||||
Optional<CppActorHandle> optional3 = Ray.getActor(childName);
|
||||
Assert.assertTrue(optional3.isPresent());
|
||||
CppActorHandle actor3 = optional3.get();
|
||||
ObjectRef<Integer> res4 = actor3.task(CppActorMethod.of("Plus1", Integer.class)).remote();
|
||||
Assert.assertEquals(res4.get(), Integer.valueOf(1));
|
||||
Object[] strings = new Object[] {"str1", "str2"};
|
||||
ObjectRef<Object[]> ref_strings =
|
||||
actor.task(CppActorMethod.of("EchoStrings", Object[].class), strings).remote();
|
||||
Assert.assertEquals(ref_strings.get(), strings);
|
||||
|
||||
if (System.getProperty("os.name").toUpperCase().indexOf("Windows") == -1) {
|
||||
// Testing the supported parameter types for c++ worker std::any type
|
||||
Object[] inputs =
|
||||
new Object[] {
|
||||
true, // Boolean
|
||||
Byte.MAX_VALUE, // Byte
|
||||
Short.MAX_VALUE, // Short
|
||||
Integer.MAX_VALUE, // Integer
|
||||
Long.MAX_VALUE, // Long
|
||||
Long.MIN_VALUE,
|
||||
BigInteger.valueOf(Long.MAX_VALUE), // BigInteger
|
||||
"Hello World!", // String
|
||||
1.234f, // Float
|
||||
1.234, // Double
|
||||
"binary".getBytes() // byte[]
|
||||
};
|
||||
ObjectRef<Object[]> ref_objs =
|
||||
actor.task(CppActorMethod.of("EchoAnyArray", Object[].class), inputs).remote();
|
||||
Object[] objs_result = ref_objs.get();
|
||||
Assert.assertEquals(objs_result.length, inputs.length);
|
||||
Assert.assertEquals((Boolean) objs_result[0], inputs[0]);
|
||||
Assert.assertEquals((Byte) objs_result[1], inputs[1]);
|
||||
Assert.assertEquals((Short) objs_result[2], inputs[2]);
|
||||
Assert.assertEquals((Integer) objs_result[3], inputs[3]);
|
||||
Assert.assertEquals((Long) objs_result[4], inputs[4]);
|
||||
Assert.assertEquals((Long) objs_result[5], inputs[5]);
|
||||
// BigInteger change to Long
|
||||
Assert.assertEquals((Long) objs_result[6], Long.MAX_VALUE);
|
||||
Assert.assertEquals((String) objs_result[7], inputs[7]);
|
||||
// Float change to double
|
||||
Assert.assertTrue(Math.abs((Double) objs_result[8] - 1.234) < 0.000001);
|
||||
Assert.assertTrue(Math.abs((Double) objs_result[9] - (Double) inputs[9]) < 0.000001);
|
||||
Assert.assertEquals((byte[]) objs_result[10], (byte[]) inputs[10]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPythonCallJavaActor() {
|
||||
ObjectRef<byte[]> res =
|
||||
Ray.task(
|
||||
PyFunction.of(PYTHON_MODULE, "py_func_call_java_actor", byte[].class),
|
||||
"1".getBytes())
|
||||
.remote();
|
||||
Assert.assertEquals(res.get(), "Counter1".getBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPassActorHandleFromPythonToJava() {
|
||||
// Call a python function which creates a python actor
|
||||
// and pass the actor handle to callPythonActorHandle.
|
||||
ObjectRef<byte[]> res =
|
||||
Ray.task(PyFunction.of(PYTHON_MODULE, "py_func_pass_python_actor_handle", byte[].class))
|
||||
.remote();
|
||||
Assert.assertEquals(res.get(), "3".getBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPassActorHandleFromJavaToPython() {
|
||||
// Create a java actor, and pass actor handle to python.
|
||||
ActorHandle<TestActor> javaActor = Ray.actor(TestActor::new, "1".getBytes()).remote();
|
||||
Preconditions.checkState(javaActor instanceof NativeActorHandle);
|
||||
ObjectRef<byte[]> res =
|
||||
Ray.task(
|
||||
PyFunction.of(PYTHON_MODULE, "py_func_call_java_actor_from_handle", byte[].class),
|
||||
javaActor)
|
||||
.remote();
|
||||
Assert.assertEquals(res.get(), "12".getBytes());
|
||||
// Create a python actor, and pass actor handle to python.
|
||||
PyActorHandle pyActor =
|
||||
Ray.actor(PyActorClass.of(PYTHON_MODULE, "Counter"), "1".getBytes()).remote();
|
||||
Preconditions.checkState(pyActor instanceof NativeActorHandle);
|
||||
res =
|
||||
Ray.task(
|
||||
PyFunction.of(PYTHON_MODULE, "py_func_call_python_actor_from_handle", byte[].class),
|
||||
pyActor)
|
||||
.remote();
|
||||
Assert.assertEquals(res.get(), "3".getBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExceptionSerialization() throws IOException {
|
||||
try {
|
||||
throw new RayException("Test Exception");
|
||||
} catch (RayException e) {
|
||||
String formattedException =
|
||||
org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace(e);
|
||||
io.ray.runtime.generated.Common.RayException exception =
|
||||
io.ray.runtime.generated.Common.RayException.parseFrom(RayExceptionSerializer.toBytes(e));
|
||||
Assert.assertEquals(exception.getFormattedExceptionString(), formattedException);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRaiseExceptionFromPython() {
|
||||
ObjectRef<Object> res =
|
||||
Ray.task(PyFunction.of(PYTHON_MODULE, "py_func_python_raise_exception", Object.class))
|
||||
.remote();
|
||||
try {
|
||||
res.get();
|
||||
} catch (RuntimeException ex) {
|
||||
// ex is a Python exception(py_func_python_raise_exception) with no cause.
|
||||
Assert.assertTrue(ex instanceof CrossLanguageException);
|
||||
CrossLanguageException e = (CrossLanguageException) ex;
|
||||
// ex.cause is null.
|
||||
Assert.assertNull(ex.getCause());
|
||||
Assert.assertTrue(
|
||||
ex.getMessage().contains("ZeroDivisionError: division by zero"), ex.getMessage());
|
||||
return;
|
||||
}
|
||||
Assert.fail();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThrowExceptionFromJava() {
|
||||
ObjectRef<Object> res =
|
||||
Ray.task(PyFunction.of(PYTHON_MODULE, "py_func_java_throw_exception", Object.class))
|
||||
.remote();
|
||||
try {
|
||||
res.get();
|
||||
} catch (RuntimeException ex) {
|
||||
final String message = ex.getMessage();
|
||||
Assert.assertTrue(message.contains("py_func_java_throw_exception"), message);
|
||||
Assert.assertTrue(
|
||||
message.contains("io.ray.test.CrossLanguageInvocationTest.throwException"), message);
|
||||
Assert.assertTrue(message.contains("java.lang.ArithmeticException: / by zero"), message);
|
||||
return;
|
||||
}
|
||||
Assert.fail();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRaiseExceptionFromNestPython() {
|
||||
ObjectRef<Object> res =
|
||||
Ray.task(PyFunction.of(PYTHON_MODULE, "py_func_nest_python_raise_exception", Object.class))
|
||||
.remote();
|
||||
try {
|
||||
res.get();
|
||||
} catch (RuntimeException ex) {
|
||||
final String message = ex.getMessage();
|
||||
Assert.assertTrue(message.contains("py_func_nest_python_raise_exception"), message);
|
||||
Assert.assertTrue(message.contains("io.ray.runtime.task.TaskExecutor.execute"), message);
|
||||
Assert.assertTrue(message.contains("py_func_python_raise_exception"), message);
|
||||
Assert.assertTrue(message.contains("ZeroDivisionError: division by zero"), message);
|
||||
return;
|
||||
}
|
||||
Assert.fail();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThrowExceptionFromNestJava() {
|
||||
ObjectRef<Object> res =
|
||||
Ray.task(PyFunction.of(PYTHON_MODULE, "py_func_nest_java_throw_exception", Object.class))
|
||||
.remote();
|
||||
try {
|
||||
res.get();
|
||||
} catch (RuntimeException ex) {
|
||||
final String message = ex.getMessage();
|
||||
Assert.assertTrue(message.contains("py_func_nest_java_throw_exception"), message);
|
||||
Assert.assertEquals(
|
||||
org.apache.commons.lang3.StringUtils.countMatches(
|
||||
message, "io.ray.api.exception.RayTaskException"),
|
||||
2);
|
||||
Assert.assertTrue(message.contains("py_func_java_throw_exception"), message);
|
||||
Assert.assertTrue(message.contains("java.lang.ArithmeticException: / by zero"), message);
|
||||
return;
|
||||
}
|
||||
Assert.fail();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallingPythonNamedActor() {
|
||||
/// 1. create Python named actor.
|
||||
/// 2. get and invoke it in Java.
|
||||
byte[] res =
|
||||
Ray.task(PyFunction.of(PYTHON_MODULE, "py_func_create_named_actor", byte[].class))
|
||||
.remote()
|
||||
.get();
|
||||
Assert.assertEquals(res, "true".getBytes(StandardCharsets.UTF_8));
|
||||
PyActorHandle pyActor = (PyActorHandle) Ray.getActor("py_named_actor").get();
|
||||
|
||||
ObjectRef<byte[]> obj =
|
||||
pyActor.task(PyActorMethod.of("increase", byte[].class), "1".getBytes()).remote();
|
||||
Assert.assertEquals(obj.get(), "102".getBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallingJavaNamedActor() {
|
||||
/// 1. create Java named actor.
|
||||
/// 2. get and invoke it in Python.
|
||||
ActorHandle<TestActor> actor =
|
||||
Ray.actor(TestActor::new, "hello".getBytes(StandardCharsets.UTF_8))
|
||||
.setName("java_named_actor")
|
||||
.remote();
|
||||
Assert.assertEquals(
|
||||
actor.task(TestActor::getValue).remote().get(), "hello".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
byte[] res =
|
||||
Ray.task(PyFunction.of(PYTHON_MODULE, "py_func_get_and_invoke_named_actor", byte[].class))
|
||||
.remote()
|
||||
.get();
|
||||
Assert.assertEquals(res, "true".getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public static Object[] pack(int i, String s, double f, Object[] o) {
|
||||
// This function will be called from test_cross_language_invocation.py
|
||||
return new Object[] {i, s, f, o};
|
||||
}
|
||||
|
||||
public static Object returnInput(Object o) {
|
||||
return o;
|
||||
}
|
||||
|
||||
public static boolean returnInputBoolean(boolean b) {
|
||||
return b;
|
||||
}
|
||||
|
||||
public static int returnInputInt(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
public static double returnInputDouble(double d) {
|
||||
return d;
|
||||
}
|
||||
|
||||
public static String returnInputString(String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
public static int[] returnInputIntArray(int[] l) {
|
||||
return l;
|
||||
}
|
||||
|
||||
public static byte[] callPythonActorHandle(PyActorHandle actor) {
|
||||
// This function will be called from test_cross_language_invocation.py
|
||||
ObjectRef<byte[]> res =
|
||||
actor.task(PyActorMethod.of("increase", byte[].class), "1".getBytes()).remote();
|
||||
Assert.assertEquals(res.get(), "3".getBytes());
|
||||
return (byte[]) res.get();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantOverflow")
|
||||
public static Object throwException() {
|
||||
return 1 / 0;
|
||||
}
|
||||
|
||||
public static Object throwJavaException() {
|
||||
ObjectRef<Object> res =
|
||||
Ray.task(PyFunction.of(PYTHON_MODULE, "py_func_java_throw_exception", Object.class))
|
||||
.remote();
|
||||
return res.get();
|
||||
}
|
||||
|
||||
public static Object raisePythonException() {
|
||||
ObjectRef<Object> res =
|
||||
Ray.task(PyFunction.of(PYTHON_MODULE, "py_func_python_raise_exception", Object.class))
|
||||
.remote();
|
||||
return res.get();
|
||||
}
|
||||
|
||||
public void testPyCallJavaOeveridedMethodWithDefault() {
|
||||
ObjectRef<Object> res =
|
||||
Ray.task(
|
||||
PyFunction.of(
|
||||
PYTHON_MODULE,
|
||||
"py_func_call_java_overrided_method_with_default_keyword",
|
||||
Object.class))
|
||||
.remote();
|
||||
Assert.assertEquals("hi", res.get());
|
||||
}
|
||||
|
||||
public void testPyCallJavaOverloadedMethodByParameterSize() {
|
||||
ObjectRef<Object> res =
|
||||
Ray.task(PyFunction.of(PYTHON_MODULE, "py_func_call_java_overloaded_method", Object.class))
|
||||
.remote();
|
||||
Assert.assertEquals(true, res.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCppActorMaxConcurrency() {
|
||||
CppActorHandle actor =
|
||||
Ray.actor(CppActorClass.of("ActorConcurrentCall::FactoryCreate", "ActorConcurrentCall"))
|
||||
.setMaxConcurrency(3)
|
||||
.remote();
|
||||
ObjectRef<String> ref1 = actor.task(CppActorMethod.of("CountDown", String.class)).remote();
|
||||
ObjectRef<String> ref2 = actor.task(CppActorMethod.of("CountDown", String.class)).remote();
|
||||
ObjectRef<String> ref3 = actor.task(CppActorMethod.of("CountDown", String.class)).remote();
|
||||
Assert.assertEquals(ref1.get(), "ok");
|
||||
Assert.assertEquals(ref2.get(), "ok");
|
||||
Assert.assertEquals(ref3.get(), "ok");
|
||||
|
||||
CppActorHandle actor2 =
|
||||
Ray.actor(CppActorClass.of("ActorConcurrentCall::FactoryCreate", "ActorConcurrentCall"))
|
||||
.setMaxConcurrency(1)
|
||||
.remote();
|
||||
ObjectRef<String> r1 = actor2.task(CppActorMethod.of("CountDown", String.class)).remote();
|
||||
ObjectRef<String> r2 = actor2.task(CppActorMethod.of("CountDown", String.class)).remote();
|
||||
ObjectRef<String> r3 = actor2.task(CppActorMethod.of("CountDown", String.class)).remote();
|
||||
Assert.expectThrows(RayTimeoutException.class, () -> r1.get(2000));
|
||||
Assert.expectThrows(RayTimeoutException.class, () -> r2.get(2000));
|
||||
Assert.expectThrows(RayTimeoutException.class, () -> r3.get(2000));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.exception.RayActorException;
|
||||
import io.ray.api.options.ActorLifetime;
|
||||
import io.ray.runtime.util.SystemUtil;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = "cluster")
|
||||
public class DefaultActorLifetimeTest {
|
||||
|
||||
private static class OwnerActor {
|
||||
private ActorHandle<ChildActor> childActor;
|
||||
|
||||
public ActorHandle<ChildActor> createChildActor(ActorLifetime childActorLifetime) {
|
||||
if (childActorLifetime == null) {
|
||||
childActor = Ray.actor(ChildActor::new).remote();
|
||||
} else {
|
||||
childActor = Ray.actor(ChildActor::new).setLifetime(childActorLifetime).remote();
|
||||
}
|
||||
if ("ok".equals(childActor.task(ChildActor::ready).remote().get())) {
|
||||
return childActor;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
int getPid() {
|
||||
return SystemUtil.pid();
|
||||
}
|
||||
|
||||
String ready() {
|
||||
return "ok";
|
||||
}
|
||||
}
|
||||
|
||||
private static class ChildActor {
|
||||
String ready() {
|
||||
return "ok";
|
||||
}
|
||||
}
|
||||
|
||||
@Test(
|
||||
groups = {"cluster"},
|
||||
dataProvider = "parameters")
|
||||
public void testDefaultActorLifetime(
|
||||
ActorLifetime defaultActorLifetime, ActorLifetime childActorLifetime)
|
||||
throws IOException, InterruptedException {
|
||||
if (defaultActorLifetime != null) {
|
||||
System.setProperty("ray.job.default-actor-lifetime", defaultActorLifetime.name());
|
||||
}
|
||||
try {
|
||||
Ray.init();
|
||||
|
||||
/// 1. create owner and invoke createChildActor.
|
||||
ActorHandle<OwnerActor> owner = Ray.actor(OwnerActor::new).remote();
|
||||
ActorHandle<ChildActor> child =
|
||||
owner.task(OwnerActor::createChildActor, childActorLifetime).remote().get();
|
||||
Assert.assertEquals("ok", child.task(ChildActor::ready).remote().get());
|
||||
int ownerPid = owner.task(OwnerActor::getPid).remote().get();
|
||||
|
||||
/// 2. Kill owner and make sure it's dead.
|
||||
Runtime.getRuntime().exec("kill -9 " + ownerPid);
|
||||
Supplier<Boolean> isOwnerDead =
|
||||
() -> {
|
||||
try {
|
||||
owner.task(OwnerActor::ready).remote().get();
|
||||
return false;
|
||||
} catch (RayActorException e) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Assert.assertTrue(TestUtils.waitForCondition(isOwnerDead, 3000));
|
||||
|
||||
/// 3. Assert child state.
|
||||
Supplier<Boolean> isChildDead =
|
||||
() -> {
|
||||
try {
|
||||
child.task(ChildActor::ready).remote().get();
|
||||
return false;
|
||||
} catch (RayActorException e) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
ActorLifetime actualLifetime = defaultActorLifetime;
|
||||
if (childActorLifetime != null) {
|
||||
actualLifetime = childActorLifetime;
|
||||
}
|
||||
Assert.assertNotNull(actualLifetime);
|
||||
if (actualLifetime == ActorLifetime.DETACHED) {
|
||||
TimeUnit.SECONDS.sleep(5);
|
||||
Assert.assertFalse(isChildDead.get());
|
||||
} else {
|
||||
Assert.assertTrue(TestUtils.waitForCondition(isChildDead, 5000));
|
||||
}
|
||||
} finally {
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider
|
||||
public static Object[][] parameters() {
|
||||
Object[] defaultEnums = new Object[] {ActorLifetime.DETACHED, ActorLifetime.NON_DETACHED};
|
||||
Object[] enums = new Object[] {null, ActorLifetime.DETACHED, ActorLifetime.NON_DETACHED};
|
||||
Object[][] params = new Object[6][2];
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
params[i][0] = defaultEnums[i / 3];
|
||||
params[i][1] = enums[i % 3];
|
||||
}
|
||||
return params;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.ray.test;
|
||||
|
||||
public class ExampleImpl implements ExampleInterface {
|
||||
|
||||
@Override
|
||||
public String echo(String str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
public String overloadedFunc(String str1) {
|
||||
return str1;
|
||||
}
|
||||
|
||||
public String overloadedFunc(String str1, String str2) {
|
||||
return str1 + str2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package io.ray.test;
|
||||
|
||||
public interface ExampleInterface {
|
||||
|
||||
default String echo(String str) {
|
||||
return "default" + str;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package io.ray.test;
|
||||
|
||||
import static io.ray.runtime.util.SystemUtil.pid;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.exception.RayActorException;
|
||||
import io.ray.api.options.ActorCreationOptions;
|
||||
import io.ray.runtime.task.TaskExecutor;
|
||||
import io.ray.runtime.util.SystemUtil;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class ExitActorTest extends BaseTest {
|
||||
|
||||
private static class ExitingActor {
|
||||
|
||||
int counter = 0;
|
||||
|
||||
public Integer incr() {
|
||||
return ++counter;
|
||||
}
|
||||
|
||||
public int getPid() {
|
||||
return pid();
|
||||
}
|
||||
|
||||
public int getSizeOfActorContextMap() {
|
||||
TaskExecutor taskExecutor = TestUtils.getRuntime().getTaskExecutor();
|
||||
try {
|
||||
Field field = TaskExecutor.class.getDeclaredField("actorContextMap");
|
||||
field.setAccessible(true);
|
||||
return ((Map<?, ?>) field.get(taskExecutor)).size();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean exit() {
|
||||
Ray.exitActor();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void testExitActor() throws IOException, InterruptedException {
|
||||
ActorHandle<ExitingActor> actor =
|
||||
Ray.actor(ExitingActor::new).setMaxRestarts(ActorCreationOptions.INFINITE_RESTART).remote();
|
||||
Assert.assertEquals(1, (int) (actor.task(ExitingActor::incr).remote().get()));
|
||||
int pid = actor.task(ExitingActor::getPid).remote().get();
|
||||
Runtime.getRuntime().exec("kill -9 " + pid);
|
||||
|
||||
while (true) {
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
try {
|
||||
actor.task(ExitingActor::getPid).remote().get();
|
||||
break;
|
||||
} catch (RayActorException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure this actor can be reconstructed.
|
||||
Assert.assertEquals(1, (int) actor.task(ExitingActor::incr).remote().get());
|
||||
|
||||
// `exitActor` will exit the actor without reconstructing.
|
||||
ObjectRef<Boolean> obj = actor.task(ExitingActor::exit).remote();
|
||||
Assert.assertThrows(RayActorException.class, obj::get);
|
||||
}
|
||||
|
||||
public void testExitActorWithDynamicOptions() {
|
||||
ActorHandle<ExitingActor> actor =
|
||||
Ray.actor(ExitingActor::new)
|
||||
.setMaxRestarts(ActorCreationOptions.INFINITE_RESTART)
|
||||
// Set dummy JVM options to start a worker process with only one worker.
|
||||
.setJvmOptions(ImmutableList.of("-Ddummy=value"))
|
||||
.remote();
|
||||
int pid = actor.task(ExitingActor::getPid).remote().get();
|
||||
Assert.assertTrue(SystemUtil.isProcessAlive(pid));
|
||||
ObjectRef<Boolean> obj1 = actor.task(ExitingActor::exit).remote();
|
||||
Assert.assertThrows(RayActorException.class, obj1::get);
|
||||
// Now the actor shouldn't be reconstructed anymore.
|
||||
Assert.assertThrows(
|
||||
RayActorException.class, () -> actor.task(ExitingActor::getPid).remote().get());
|
||||
// Now the worker process should be dead.
|
||||
Assert.assertTrue(TestUtils.waitForCondition(() -> !SystemUtil.isProcessAlive(pid), 5000));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package io.ray.test;
|
||||
|
||||
import static io.ray.runtime.util.SystemUtil.pid;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.runtime.util.SystemUtil;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class ExitActorTest2 extends BaseTest {
|
||||
|
||||
private static class ExitingActor {
|
||||
private final Thread thread;
|
||||
|
||||
public ExitingActor() {
|
||||
thread =
|
||||
new Thread(
|
||||
() -> {
|
||||
try {
|
||||
TimeUnit.MINUTES.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
// Start a non-daemon thread
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public int getPid() {
|
||||
return pid();
|
||||
}
|
||||
|
||||
public void exit() {
|
||||
Ray.exitActor();
|
||||
}
|
||||
}
|
||||
|
||||
public void testExitActorWithUserCreatedThread() {
|
||||
ActorHandle<ExitingActor> actor = Ray.actor(ExitingActor::new).remote();
|
||||
int pid = actor.task(ExitingActor::getPid).remote().get();
|
||||
Assert.assertTrue(SystemUtil.isProcessAlive(pid));
|
||||
actor.task(ExitingActor::exit).remote();
|
||||
Assert.assertTrue(TestUtils.waitForCondition(() -> !SystemUtil.isProcessAlive(pid), 10000));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.exception.RayActorException;
|
||||
import io.ray.api.exception.RayTaskException;
|
||||
import io.ray.api.exception.RayWorkerException;
|
||||
import io.ray.api.exception.UnreconstructableException;
|
||||
import io.ray.api.function.RayFunc0;
|
||||
import io.ray.api.id.ObjectId;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class FailureTest extends BaseTest {
|
||||
|
||||
private static final String EXCEPTION_MESSAGE = "Oops";
|
||||
|
||||
public static int badFunc() {
|
||||
throw new RuntimeException(EXCEPTION_MESSAGE);
|
||||
}
|
||||
|
||||
public static int badFunc2() {
|
||||
System.exit(-1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int slowFunc() {
|
||||
try {
|
||||
Thread.sleep(10000);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int echo(int obj) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static class BadActor {
|
||||
|
||||
public BadActor(boolean failOnCreation) {
|
||||
if (failOnCreation) {
|
||||
throw new RuntimeException(EXCEPTION_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
public int badMethod() {
|
||||
throw new RuntimeException(EXCEPTION_MESSAGE);
|
||||
}
|
||||
|
||||
public int badMethod2() {
|
||||
System.exit(-1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SlowActor {
|
||||
public SlowActor(ActorHandle<SignalActor> signalActor) {
|
||||
if (Ray.getRuntimeContext().wasCurrentActorRestarted()) {
|
||||
signalActor.task(SignalActor::waitSignal).remote().get();
|
||||
}
|
||||
}
|
||||
|
||||
public String ping() {
|
||||
return "pong";
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertTaskFailedWithRayTaskException(ObjectRef<?> objectRef) {
|
||||
try {
|
||||
objectRef.get();
|
||||
Assert.fail("Task didn't fail.");
|
||||
} catch (RayTaskException e) {
|
||||
Throwable rootCause = e.getCause();
|
||||
while (rootCause.getCause() != null) {
|
||||
rootCause = rootCause.getCause();
|
||||
}
|
||||
Assert.assertTrue(rootCause instanceof RuntimeException);
|
||||
Assert.assertEquals(rootCause.getMessage(), EXCEPTION_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertTaskFailedWithRayActorException(ObjectRef<?> objectRef) {
|
||||
try {
|
||||
objectRef.get();
|
||||
Assert.fail("Task didn't fail.");
|
||||
} catch (RayActorException e) {
|
||||
Throwable rootCause = e;
|
||||
while (rootCause.getCause() != null) {
|
||||
rootCause = rootCause.getCause();
|
||||
}
|
||||
Assert.assertTrue(rootCause instanceof RuntimeException);
|
||||
Assert.assertTrue(rootCause.getMessage().contains(EXCEPTION_MESSAGE));
|
||||
}
|
||||
}
|
||||
|
||||
public void testNormalTaskFailure() {
|
||||
assertTaskFailedWithRayTaskException(Ray.task(FailureTest::badFunc).remote());
|
||||
}
|
||||
|
||||
public void testActorCreationFailure() {
|
||||
ActorHandle<BadActor> actor = Ray.actor(BadActor::new, true).remote();
|
||||
assertTaskFailedWithRayActorException(actor.task(BadActor::badMethod).remote());
|
||||
}
|
||||
|
||||
public void testActorTaskFailure() {
|
||||
ActorHandle<BadActor> actor = Ray.actor(BadActor::new, false).remote();
|
||||
assertTaskFailedWithRayTaskException(actor.task(BadActor::badMethod).remote());
|
||||
}
|
||||
|
||||
public void testWorkerProcessDying() {
|
||||
try {
|
||||
Ray.task(FailureTest::badFunc2).remote().get();
|
||||
Assert.fail("This line shouldn't be reached.");
|
||||
} catch (RayWorkerException e) {
|
||||
// When the worker process dies while executing a task, we should receive an
|
||||
// RayWorkerException.
|
||||
}
|
||||
}
|
||||
|
||||
public void testActorProcessDying() {
|
||||
ActorHandle<BadActor> actor = Ray.actor(BadActor::new, false).remote();
|
||||
try {
|
||||
actor.task(BadActor::badMethod2).remote().get();
|
||||
Assert.fail("This line shouldn't be reached.");
|
||||
} catch (RayActorException e) {
|
||||
// When the actor process dies while executing a task, we should receive an
|
||||
// RayActorException.
|
||||
Assert.assertEquals(e.actorId, actor.getId());
|
||||
}
|
||||
try {
|
||||
actor.task(BadActor::badMethod).remote().get();
|
||||
Assert.fail("This line shouldn't be reached.");
|
||||
} catch (RayActorException e) {
|
||||
// When a actor task is submitted to a dead actor, we should also receive an
|
||||
// RayActorException.
|
||||
}
|
||||
}
|
||||
|
||||
public void testActorTaskFastFail() throws IOException, InterruptedException {
|
||||
ActorHandle<SignalActor> signalActor = SignalActor.create();
|
||||
// NOTE(kfstorm): Currently, `max_task_retries` is always 0 for actors created in Java.
|
||||
// Once `max_task_retries` is configurable in Java, we'd better set it to 0 explicitly to show
|
||||
// the test scenario.
|
||||
ActorHandle<SlowActor> actor =
|
||||
Ray.actor(SlowActor::new, signalActor).setMaxRestarts(1).remote();
|
||||
actor.task(SlowActor::ping).remote().get();
|
||||
actor.kill(/*noRestart=*/ false);
|
||||
|
||||
// Wait for a while so that now the driver knows the actor is in RESTARTING state.
|
||||
Thread.sleep(1000);
|
||||
// An actor task should fail quickly until the actor is restarted.
|
||||
Assert.expectThrows(RayActorException.class, () -> actor.task(SlowActor::ping).remote().get());
|
||||
|
||||
signalActor.task(SignalActor::sendSignal).remote().get();
|
||||
// Wait for a while so that now the driver knows the actor is in ALIVE state.
|
||||
Thread.sleep(1000);
|
||||
// An actor task should succeed.
|
||||
actor.task(SlowActor::ping).remote().get();
|
||||
}
|
||||
|
||||
public void testGetThrowsQuicklyWhenFoundException() {
|
||||
List<RayFunc0<Integer>> badFunctions =
|
||||
Arrays.asList(FailureTest::badFunc, FailureTest::badFunc2);
|
||||
TestUtils.warmUpCluster();
|
||||
for (RayFunc0<Integer> badFunc : badFunctions) {
|
||||
ObjectRef<Integer> obj1 = Ray.task(badFunc).remote();
|
||||
ObjectRef<Integer> obj2 = Ray.task(FailureTest::slowFunc).remote();
|
||||
TestUtils.executeWithinTime(
|
||||
() ->
|
||||
Assert.expectThrows(RuntimeException.class, () -> Ray.get(Arrays.asList(obj1, obj2))),
|
||||
5000);
|
||||
}
|
||||
}
|
||||
|
||||
public void testExceptionSerialization() {
|
||||
RayTaskException ex1 =
|
||||
Assert.expectThrows(
|
||||
RayTaskException.class,
|
||||
() -> {
|
||||
Ray.put(new RayTaskException(10008, "localhost", "xxx", new RayActorException()))
|
||||
.get();
|
||||
});
|
||||
Assert.assertEquals(ex1.getCause().getClass(), RayActorException.class);
|
||||
RayTaskException ex2 =
|
||||
Assert.expectThrows(
|
||||
RayTaskException.class,
|
||||
() -> {
|
||||
Ray.put(new RayTaskException(10008, "localhost", "xxx", new RayWorkerException()))
|
||||
.get();
|
||||
});
|
||||
Assert.assertEquals(ex2.getCause().getClass(), RayWorkerException.class);
|
||||
|
||||
ObjectId objectId = ObjectId.fromRandom();
|
||||
RayTaskException ex3 =
|
||||
Assert.expectThrows(
|
||||
RayTaskException.class,
|
||||
() -> {
|
||||
Ray.put(
|
||||
new RayTaskException(
|
||||
10008, "localhost", "xxx", new UnreconstructableException(objectId)))
|
||||
.get();
|
||||
});
|
||||
Assert.assertEquals(ex3.getCause().getClass(), UnreconstructableException.class);
|
||||
Assert.assertEquals(((UnreconstructableException) ex3.getCause()).objectId, objectId);
|
||||
}
|
||||
|
||||
public void testTaskChainWithException() {
|
||||
ObjectRef<Integer> obj1 = Ray.task(FailureTest::badFunc).remote();
|
||||
ObjectRef<Integer> obj2 = Ray.task(FailureTest::echo, obj1).remote();
|
||||
RayTaskException ex = Assert.expectThrows(RayTaskException.class, () -> Ray.get(obj2));
|
||||
Assert.assertTrue(ex.getCause() instanceof RayTaskException);
|
||||
RayTaskException ex2 = (RayTaskException) ex.getCause();
|
||||
Assert.assertTrue(ex2.getCause() instanceof RuntimeException);
|
||||
RuntimeException ex3 = (RuntimeException) ex2.getCause();
|
||||
Assert.assertEquals(EXCEPTION_MESSAGE, ex3.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.ray.api.id.JobId;
|
||||
import io.ray.api.runtimecontext.NodeInfo;
|
||||
import io.ray.runtime.config.RayConfig;
|
||||
import io.ray.runtime.gcs.GcsClient;
|
||||
import java.util.List;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.testng.Assert;
|
||||
import org.testng.SkipException;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class GcsClientTest extends BaseTest {
|
||||
|
||||
@BeforeClass
|
||||
public void setUp() {
|
||||
if (SystemUtils.IS_OS_MAC) {
|
||||
throw new SkipException("Skip NodeIpTest on Mac OS");
|
||||
}
|
||||
System.setProperty("ray.head-args.0", "--resources={\"A\":8}");
|
||||
}
|
||||
|
||||
public void testGetAllNodeInfo() {
|
||||
RayConfig config = TestUtils.getRuntime().getRayConfig();
|
||||
|
||||
Preconditions.checkNotNull(config);
|
||||
GcsClient gcsClient = TestUtils.getRuntime().getGcsClient();
|
||||
List<NodeInfo> allNodeInfo = gcsClient.getAllNodeInfo();
|
||||
Assert.assertEquals(allNodeInfo.size(), 1);
|
||||
Assert.assertEquals(allNodeInfo.get(0).nodeAddress, config.nodeIp);
|
||||
Assert.assertTrue(allNodeInfo.get(0).isAlive);
|
||||
Assert.assertEquals((double) allNodeInfo.get(0).resources.get("A"), 8.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNextJob() {
|
||||
RayConfig config = TestUtils.getRuntime().getRayConfig();
|
||||
// The value of job id of this driver in cluster should be 1.
|
||||
Assert.assertEquals(config.getJobId(), JobId.fromInt(1));
|
||||
|
||||
GcsClient gcsClient = TestUtils.getRuntime().getGcsClient();
|
||||
for (int i = 2; i < 100; ++i) {
|
||||
Assert.assertEquals(gcsClient.nextJobId(), JobId.fromInt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.id.JobId;
|
||||
import io.ray.api.runtimecontext.ActorInfo;
|
||||
import io.ray.api.runtimecontext.ActorState;
|
||||
import io.ray.api.runtimecontext.Address;
|
||||
import io.ray.api.runtimecontext.NodeInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class GetActorInfoTest extends BaseTest {
|
||||
|
||||
@BeforeClass
|
||||
public void setUp() {
|
||||
System.setProperty("ray.head-args.0", "--num-cpus=5");
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public void tearDown() {
|
||||
System.clearProperty("ray.head-args.0");
|
||||
}
|
||||
|
||||
private static class Echo {
|
||||
|
||||
public String echo(String str) {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetAllActorInfo() {
|
||||
List<NodeInfo> nodes = Ray.getRuntimeContext().getAllNodeInfo();
|
||||
Assert.assertEquals(nodes.size(), 1);
|
||||
final NodeInfo thisNode = nodes.get(0);
|
||||
final Address thisAddress =
|
||||
new Address(thisNode.nodeId, thisNode.nodeAddress, thisNode.nodeManagerPort);
|
||||
|
||||
final int numActors = 5;
|
||||
ArrayList<ActorHandle<Echo>> echos = new ArrayList<>(numActors);
|
||||
for (int i = 0; i < numActors; ++i) {
|
||||
ActorHandle<Echo> echo = Ray.actor(Echo::new).remote();
|
||||
echos.add(echo);
|
||||
}
|
||||
|
||||
ArrayList<ObjectRef<String>> objs = new ArrayList<>();
|
||||
echos.forEach(echo -> objs.add(echo.task(Echo::echo, "hello").remote()));
|
||||
Ray.wait(objs);
|
||||
|
||||
List<ActorInfo> actorInfo = Ray.getRuntimeContext().getAllActorInfo();
|
||||
Assert.assertEquals(actorInfo.size(), 5);
|
||||
actorInfo.forEach(
|
||||
info -> {
|
||||
Assert.assertEquals(info.name, "");
|
||||
Assert.assertTrue(echos.stream().anyMatch(echo -> echo.getId().equals(info.actorId)));
|
||||
Assert.assertEquals(info.numRestarts, 0);
|
||||
/// Because `node.nodeManagerPort` is not `actorInfo.address.port`, we should
|
||||
/// not just equal them.
|
||||
Assert.assertEquals(info.address.nodeId, thisAddress.nodeId);
|
||||
Assert.assertEquals(info.address.ip, thisAddress.ip);
|
||||
});
|
||||
}
|
||||
|
||||
public void testGetActorsByJobIdAndState() {
|
||||
final int numActors = 5;
|
||||
ArrayList<ActorHandle<Echo>> echos = new ArrayList<>(numActors);
|
||||
for (int i = 0; i < numActors; ++i) {
|
||||
ActorHandle<Echo> echo = Ray.actor(Echo::new).setResource("CPU", 1.0).remote();
|
||||
echos.add(echo);
|
||||
}
|
||||
ArrayList<ObjectRef<String>> objs = new ArrayList<>();
|
||||
echos.forEach(echo -> objs.add(echo.task(Echo::echo, "hello").remote()));
|
||||
Ray.get(objs, 10000);
|
||||
|
||||
// get all actors
|
||||
List<ActorInfo> actorInfo = Ray.getRuntimeContext().getAllActorInfo(null, null);
|
||||
Assert.assertEquals(actorInfo.size(), 5);
|
||||
|
||||
// Filtered by job id
|
||||
JobId jobId = Ray.getRuntimeContext().getCurrentJobId();
|
||||
actorInfo = Ray.getRuntimeContext().getAllActorInfo(jobId, null);
|
||||
Assert.assertEquals(actorInfo.size(), 5);
|
||||
|
||||
// Filtered by actor sate
|
||||
actorInfo = Ray.getRuntimeContext().getAllActorInfo(null, ActorState.ALIVE);
|
||||
Assert.assertEquals(actorInfo.size(), 5);
|
||||
|
||||
actorInfo = Ray.getRuntimeContext().getAllActorInfo(null, ActorState.DEPENDENCIES_UNREADY);
|
||||
Assert.assertEquals(actorInfo.size(), 0);
|
||||
|
||||
actorInfo = Ray.getRuntimeContext().getAllActorInfo(null, ActorState.RESTARTING);
|
||||
Assert.assertEquals(actorInfo.size(), 0);
|
||||
|
||||
ActorHandle<Echo> actor = Ray.actor(Echo::new).setResource("CPU", 1.0).remote();
|
||||
Assert.assertTrue(
|
||||
TestUtils.waitForCondition(
|
||||
() ->
|
||||
Ray.getRuntimeContext().getAllActorInfo(null, ActorState.PENDING_CREATION).size()
|
||||
== 1,
|
||||
5000));
|
||||
|
||||
actor.kill(true);
|
||||
Assert.assertTrue(
|
||||
TestUtils.waitForCondition(
|
||||
() -> Ray.getRuntimeContext().getAllActorInfo(null, ActorState.DEAD).size() == 1,
|
||||
5000));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test
|
||||
public class GetCurrentActorHandleTest extends BaseTest {
|
||||
|
||||
private static class Echo {
|
||||
|
||||
public Echo() {}
|
||||
|
||||
public ObjectRef<String> echo(String str) {
|
||||
ActorHandle<Echo> self = Ray.getRuntimeContext().getCurrentActorHandle();
|
||||
return self.task(Echo::echo2, str).remote();
|
||||
}
|
||||
|
||||
public String echo2(String str) {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCurrentActorHandle() {
|
||||
ActorHandle<Echo> echo1 = Ray.actor(Echo::new).remote();
|
||||
ObjectRef<String> obj = echo1.task(Echo::echo, "hello").remote().get();
|
||||
Assert.assertEquals("hello", obj.get());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.exception.RayTimeoutException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test
|
||||
public class GetTimeoutTest extends BaseTest {
|
||||
|
||||
public static int squareDelay(int x) {
|
||||
try {
|
||||
Thread.sleep(5 * 1000);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return x * x;
|
||||
}
|
||||
|
||||
public static class Counter {
|
||||
|
||||
private int value;
|
||||
|
||||
public Counter(int initValue) {
|
||||
this.value = initValue;
|
||||
}
|
||||
|
||||
public int getValueDelay() {
|
||||
try {
|
||||
Thread.sleep(5 * 1000);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public void testActorTaskGetTimeout() {
|
||||
ActorHandle<Counter> actor = Ray.actor(Counter::new, 1).remote();
|
||||
Assert.assertEquals(Integer.valueOf(1), actor.task(Counter::getValueDelay).remote().get());
|
||||
Assert.assertEquals(Integer.valueOf(1), actor.task(Counter::getValueDelay).remote().get(10000));
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
long timeout = 2000;
|
||||
Assert.assertThrows(
|
||||
RayTimeoutException.class,
|
||||
() -> Ray.get(actor.task(Counter::getValueDelay).remote(), timeout));
|
||||
long waitTime = System.currentTimeMillis() - startTime;
|
||||
Assert.assertTrue(Math.abs(waitTime - timeout) < 1500);
|
||||
}
|
||||
|
||||
public void testNormalTaskGetTimeout() {
|
||||
List<ObjectRef<Integer>> objectRefList = new ArrayList<>();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
objectRefList.add(Ray.task(GetTimeoutTest::squareDelay, i).remote());
|
||||
}
|
||||
long startTime = System.currentTimeMillis();
|
||||
long timeout = 2000;
|
||||
Assert.assertThrows(RayTimeoutException.class, () -> Ray.get(objectRefList, timeout));
|
||||
long waitTime = System.currentTimeMillis() - startTime;
|
||||
Assert.assertTrue(Math.abs(waitTime - timeout) < 1500);
|
||||
Assert.assertEquals(Ray.get(objectRefList, 10000).toArray(), new int[] {0, 1, 4, 9});
|
||||
Assert.assertEquals(Ray.get(objectRefList).toArray(), new int[] {0, 1, 4, 9});
|
||||
}
|
||||
|
||||
public void testObjectGetTimeout() {
|
||||
int y = 1;
|
||||
ObjectRef<Integer> objectRef = Ray.put(y);
|
||||
Assert.assertTrue(Ray.get(objectRef) == 1);
|
||||
Assert.assertTrue(Ray.get(objectRef, 2000) == 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class GlobalGcTest extends BaseTest {
|
||||
|
||||
@BeforeClass
|
||||
public void setUp() {
|
||||
System.setProperty("ray.head-args.0", "--object-store-memory=" + 140L * 1024 * 1024);
|
||||
}
|
||||
|
||||
public static class LargeObjectWithCyclicRef {
|
||||
|
||||
private final LargeObjectWithCyclicRef loop;
|
||||
|
||||
private final ObjectRef<TestUtils.LargeObject> largeObject;
|
||||
|
||||
public LargeObjectWithCyclicRef() {
|
||||
this.loop = this;
|
||||
this.largeObject = Ray.put(new TestUtils.LargeObject(40 * 1024 * 1024));
|
||||
}
|
||||
}
|
||||
|
||||
public static class GarbageHolder {
|
||||
|
||||
// Hold a strong reference initially so that the JVM cannot collect the
|
||||
// cyclic object before the test explicitly triggers the global GC event
|
||||
// below. This mirrors the `gc.disable()` call used in the Python
|
||||
// equivalent of this test (see python/ray/tests/test_global_gc.py).
|
||||
private LargeObjectWithCyclicRef strongRef;
|
||||
|
||||
private WeakReference<LargeObjectWithCyclicRef> garbage;
|
||||
|
||||
public GarbageHolder() {
|
||||
strongRef = new LargeObjectWithCyclicRef();
|
||||
garbage = new WeakReference<>(strongRef);
|
||||
}
|
||||
|
||||
public boolean hasGarbage() {
|
||||
return garbage.get() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the strong reference so the cyclic object becomes eligible for collection by the next
|
||||
* JVM GC. Call this immediately before triggering the global GC event the test is exercising.
|
||||
* Returns a boolean so the caller can `.get()` on the resulting ObjectRef to synchronously wait
|
||||
* for the release to take effect on the actor process; Ray's Java API does not surface an
|
||||
* ObjectRef for void actor methods.
|
||||
*/
|
||||
public boolean releaseStrongRef() {
|
||||
strongRef = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public TestUtils.LargeObject returnLargeObject() {
|
||||
return new TestUtils.LargeObject(80 * 1024 * 1024);
|
||||
}
|
||||
}
|
||||
|
||||
private void testGlobalGcWhenFull(boolean withPut) {
|
||||
// Local driver. Hold a strong reference until we explicitly want to allow
|
||||
// GC (mirrors `gc.disable()` in the Python equivalent test).
|
||||
LargeObjectWithCyclicRef localStrong = new LargeObjectWithCyclicRef();
|
||||
WeakReference<LargeObjectWithCyclicRef> localRef = new WeakReference<>(localStrong);
|
||||
|
||||
// Remote workers.
|
||||
List<ActorHandle<GarbageHolder>> actors =
|
||||
IntStream.range(0, 2)
|
||||
.mapToObj(i -> Ray.actor(GarbageHolder::new).remote())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Assert.assertNotNull(localRef.get());
|
||||
for (ActorHandle<GarbageHolder> actor : actors) {
|
||||
Assert.assertTrue(actor.task(GarbageHolder::hasGarbage).remote().get());
|
||||
}
|
||||
|
||||
// Now release the strong references so the cyclic objects become eligible
|
||||
// for collection once the global GC event is triggered below.
|
||||
localStrong = null;
|
||||
for (ActorHandle<GarbageHolder> actor : actors) {
|
||||
actor.task(GarbageHolder::releaseStrongRef).remote().get();
|
||||
}
|
||||
|
||||
if (withPut) {
|
||||
// GC should be triggered for all workers, including the local driver,
|
||||
// when the driver tries to Ray.put a value that doesn't fit in the
|
||||
// object store. This should cause the captured ObjectRefs to be evicted.
|
||||
Ray.put(new TestUtils.LargeObject(80 * 1024 * 1024));
|
||||
} else {
|
||||
// GC should be triggered for all workers, including the local driver,
|
||||
// when a remote task tries to put a return value that doesn't fit in
|
||||
// the object store. This should cause the captured ObjectRefs' to be evicted.
|
||||
actors.get(0).task(GarbageHolder::returnLargeObject).remote().get();
|
||||
}
|
||||
|
||||
TestUtils.waitForCondition(
|
||||
() ->
|
||||
localRef.get() == null
|
||||
&& actors.stream().noneMatch(a -> a.task(GarbageHolder::hasGarbage).remote().get()),
|
||||
10 * 1000);
|
||||
}
|
||||
|
||||
public void testGlobalGcWhenFullWithPut() {
|
||||
testGlobalGcWhenFull(true);
|
||||
}
|
||||
|
||||
public void testGlobalGcWhenFullWithReturn() {
|
||||
testGlobalGcWhenFull(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import java.util.List;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class GpuResourceTest {
|
||||
private static class GpuActor {
|
||||
public List<Long> getGpuResourceFunc() {
|
||||
return Ray.getRuntimeContext().getGpuIds();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(groups = "cluster")
|
||||
public void testGetResourceId() {
|
||||
try {
|
||||
System.setProperty("ray.head-args.0", "--num-gpus=3");
|
||||
Ray.init();
|
||||
ActorHandle<GpuActor> gpuActorActorHandle =
|
||||
Ray.actor(GpuActor::new).setResource("GPU", 1D).remote();
|
||||
List<Long> gpuIds = gpuActorActorHandle.task(GpuActor::getGpuResourceFunc).remote().get();
|
||||
ActorHandle<GpuActor> gpuActorActorHandle2 =
|
||||
Ray.actor(GpuActor::new).setResource("GPU", 1D).remote();
|
||||
List<Long> gpuIds2 = gpuActorActorHandle2.task(GpuActor::getGpuResourceFunc).remote().get();
|
||||
ActorHandle<GpuActor> gpuActorActorHandle3 =
|
||||
Ray.actor(GpuActor::new).setResource("GPU", 0.5D).remote();
|
||||
List<Long> gpuIds3 = gpuActorActorHandle3.task(GpuActor::getGpuResourceFunc).remote().get();
|
||||
ActorHandle<GpuActor> gpuActorActorHandle4 =
|
||||
Ray.actor(GpuActor::new).setResource("GPU", 0.5D).remote();
|
||||
List<Long> gpuIds4 = gpuActorActorHandle4.task(GpuActor::getGpuResourceFunc).remote().get();
|
||||
Assert.assertNotEquals(gpuIds.get(0), gpuIds2.get(0));
|
||||
Assert.assertEquals(gpuIds3.get(0), gpuIds4.get(0));
|
||||
} finally {
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/** Hello world. */
|
||||
public class HelloWorldTest extends BaseTest {
|
||||
|
||||
private static String hello() {
|
||||
return "hello";
|
||||
}
|
||||
|
||||
private static String world() {
|
||||
return "world!";
|
||||
}
|
||||
|
||||
private static String merge(String hello, String world) {
|
||||
return hello + "," + world;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHelloWorld() {
|
||||
ObjectRef<String> hello = Ray.task(HelloWorldTest::hello).remote();
|
||||
ObjectRef<String> world = Ray.task(HelloWorldTest::world).remote();
|
||||
String helloWorld = Ray.task(HelloWorldTest::merge, hello, world).remote().get();
|
||||
Assert.assertEquals("hello,world!", helloWorld);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class JobConfigTest extends BaseTest {
|
||||
|
||||
@BeforeClass
|
||||
public void setupJobConfig() {
|
||||
System.setProperty("ray.job.jvm-options.0", "-DX=999");
|
||||
System.setProperty("ray.job.jvm-options.1", "-DY=998");
|
||||
}
|
||||
|
||||
public static String getJvmOptions(String propertyName) {
|
||||
return System.getProperty(propertyName);
|
||||
}
|
||||
|
||||
public static class MyActor {
|
||||
|
||||
public String getJvmOptions(String propertyName) {
|
||||
return System.getProperty(propertyName);
|
||||
}
|
||||
}
|
||||
|
||||
public void testJvmOptions() {
|
||||
Assert.assertEquals("999", Ray.task(JobConfigTest::getJvmOptions, "X").remote().get());
|
||||
Assert.assertEquals("998", Ray.task(JobConfigTest::getJvmOptions, "Y").remote().get());
|
||||
}
|
||||
|
||||
public void testInActor() {
|
||||
ActorHandle<MyActor> actor = Ray.actor(MyActor::new).remote();
|
||||
|
||||
// test jvm options.
|
||||
Assert.assertEquals("999", actor.task(MyActor::getJvmOptions, "X").remote().get());
|
||||
Assert.assertEquals("998", actor.task(MyActor::getJvmOptions, "Y").remote().get());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.exception.RayActorException;
|
||||
import java.util.function.BiConsumer;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class KillActorTest extends BaseTest {
|
||||
|
||||
public static class HangActor {
|
||||
|
||||
public String ping() {
|
||||
return "pong";
|
||||
}
|
||||
|
||||
public boolean hang() throws InterruptedException {
|
||||
while (true) {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class KillerActor {
|
||||
|
||||
public void kill(ActorHandle<?> actor, boolean noRestart) {
|
||||
actor.kill(noRestart);
|
||||
}
|
||||
|
||||
public void killWithoutRestart(ActorHandle<?> actor) {
|
||||
actor.kill();
|
||||
}
|
||||
}
|
||||
|
||||
private static void localKill(ActorHandle<?> actor, boolean noRestart) {
|
||||
actor.kill(noRestart);
|
||||
}
|
||||
|
||||
private static void remoteKill(ActorHandle<?> actor, boolean noRestart) {
|
||||
ActorHandle<KillerActor> killer = Ray.actor(KillerActor::new).remote();
|
||||
killer.task(KillerActor::kill, actor, noRestart).remote();
|
||||
}
|
||||
|
||||
private void testKillActor(BiConsumer<ActorHandle<?>, Boolean> kill, boolean noRestart) {
|
||||
ActorHandle<HangActor> actor = Ray.actor(HangActor::new).setMaxRestarts(1).remote();
|
||||
// Wait for the actor to be created.
|
||||
actor.task(HangActor::ping).remote().get();
|
||||
ObjectRef<Boolean> result = actor.task(HangActor::hang).remote();
|
||||
// The actor will hang in this task.
|
||||
Assert.assertEquals(0, Ray.wait(ImmutableList.of(result), 1, 500).getReady().size());
|
||||
|
||||
// Kill the actor
|
||||
kill.accept(actor, noRestart);
|
||||
// The get operation will fail with RayActorException
|
||||
Assert.expectThrows(RayActorException.class, result::get);
|
||||
|
||||
try {
|
||||
// Sleep 1s here to make sure the driver has received the actor notification
|
||||
// (of state RESTARTING or DEAD).
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if (noRestart) {
|
||||
// The actor should not be restarted.
|
||||
Assert.expectThrows(
|
||||
RayActorException.class, () -> actor.task(HangActor::hang).remote().get());
|
||||
} else {
|
||||
Assert.assertEquals(actor.task(HangActor::ping).remote().get(), "pong");
|
||||
}
|
||||
}
|
||||
|
||||
public void testLocalKill() {
|
||||
testKillActor(KillActorTest::localKill, false);
|
||||
testKillActor(KillActorTest::localKill, true);
|
||||
testKillActor((actorHandle, noRestart) -> actorHandle.kill(), true);
|
||||
}
|
||||
|
||||
public void testRemoteKill() {
|
||||
testKillActor(KillActorTest::remoteKill, false);
|
||||
testKillActor(KillActorTest::remoteKill, true);
|
||||
testKillActor(
|
||||
(actor, noRestart) -> {
|
||||
ActorHandle<KillerActor> killer = Ray.actor(KillerActor::new).remote();
|
||||
killer.task(KillerActor::killWithoutRestart, actor).remote();
|
||||
},
|
||||
true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.id.ActorId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class LocalModeTest extends BaseTest {
|
||||
|
||||
private static final int NUM_ACTOR_INSTANCE = 10;
|
||||
|
||||
private static final int TIMES_TO_CALL_PER_ACTOR = 10;
|
||||
|
||||
static class MyActor {
|
||||
public MyActor() {}
|
||||
|
||||
public long getThreadId() {
|
||||
return Thread.currentThread().getId();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(groups = {"local"})
|
||||
public void testActorTasksInOneThread() {
|
||||
List<ActorHandle<MyActor>> actors = new ArrayList<>();
|
||||
Map<ActorId, Long> actorThreadIds = new HashMap<>();
|
||||
for (int i = 0; i < NUM_ACTOR_INSTANCE; ++i) {
|
||||
ActorHandle<MyActor> actor = Ray.actor(MyActor::new).remote();
|
||||
actors.add(actor);
|
||||
actorThreadIds.put(actor.getId(), actor.task(MyActor::getThreadId).remote().get());
|
||||
}
|
||||
|
||||
Map<ActorId, List<ObjectRef<Long>>> allResults = new HashMap<>();
|
||||
for (int i = 0; i < NUM_ACTOR_INSTANCE; ++i) {
|
||||
final ActorHandle<MyActor> actor = actors.get(i);
|
||||
List<ObjectRef<Long>> thisActorResult = new ArrayList<>();
|
||||
for (int j = 0; j < TIMES_TO_CALL_PER_ACTOR; ++j) {
|
||||
thisActorResult.add(actor.task(MyActor::getThreadId).remote());
|
||||
}
|
||||
allResults.put(actor.getId(), thisActorResult);
|
||||
}
|
||||
|
||||
// check result.
|
||||
for (int i = 0; i < NUM_ACTOR_INSTANCE; ++i) {
|
||||
final ActorHandle<MyActor> actor = actors.get(i);
|
||||
final List<ObjectRef<Long>> thisActorResult = allResults.get(actor.getId());
|
||||
// assert
|
||||
for (ObjectRef<Long> threadId : thisActorResult) {
|
||||
Assert.assertEquals(threadId.get(), actorThreadIds.get(actor.getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class LoggingPerfTest extends BaseTest {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(LoggingPerfTest.class);
|
||||
|
||||
private static class LoggingPerfActor {
|
||||
|
||||
public Long measure() {
|
||||
final int count = 2000000;
|
||||
final long startMs = System.currentTimeMillis();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
LOG.info("hello world");
|
||||
}
|
||||
final long endMs = System.currentTimeMillis();
|
||||
return endMs - startMs;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoggingPerf() {
|
||||
ActorHandle<LoggingPerfActor> actor = Ray.actor(LoggingPerfActor::new).remote();
|
||||
ObjectRef<Long> took = actor.task(LoggingPerfActor::measure).remote();
|
||||
LOG.info("It took {} milliseconds.", took.get());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.ray.runtime.metric.Count;
|
||||
import io.ray.runtime.metric.Gauge;
|
||||
import io.ray.runtime.metric.Histogram;
|
||||
import io.ray.runtime.metric.MetricConfig;
|
||||
import io.ray.runtime.metric.Metrics;
|
||||
import io.ray.runtime.metric.Sum;
|
||||
import io.ray.runtime.metric.TagKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.AfterMethod;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class MetricTest extends BaseTest {
|
||||
|
||||
boolean doubleEqual(double value, double other) {
|
||||
return value <= other + 1e-5 && value >= other - 1e-5;
|
||||
}
|
||||
|
||||
private MetricConfig initRayMetrics(
|
||||
long timeIntervalMs, int threadPoolSize, long shutdownWaitTimeMs) {
|
||||
MetricConfig config =
|
||||
MetricConfig.builder()
|
||||
.timeIntervalMs(timeIntervalMs)
|
||||
.threadPoolSize(threadPoolSize)
|
||||
.shutdownWaitTimeMs(shutdownWaitTimeMs)
|
||||
.create();
|
||||
Metrics.init(config);
|
||||
return config;
|
||||
}
|
||||
|
||||
private Gauge registerGauge() {
|
||||
return Metrics.gauge()
|
||||
.name("metric_gauge")
|
||||
.description("gauge")
|
||||
.unit("")
|
||||
.tags(ImmutableMap.of("tag1", "value1"))
|
||||
.register();
|
||||
}
|
||||
|
||||
private Count registerCount() {
|
||||
return Metrics.count()
|
||||
.name("metric_count")
|
||||
.description("counter")
|
||||
.unit("1pc")
|
||||
.tags(ImmutableMap.of("tag1", "value1", "count_tag", "default"))
|
||||
.register();
|
||||
}
|
||||
|
||||
private Sum registerSum() {
|
||||
return Metrics.sum()
|
||||
.name("metric_sum")
|
||||
.description("sum")
|
||||
.unit("1pc")
|
||||
.tags(ImmutableMap.of("tag1", "value1", "sum_tag", "default"))
|
||||
.register();
|
||||
}
|
||||
|
||||
private Histogram registerHistogram() {
|
||||
return Metrics.histogram()
|
||||
.name("metric_histogram")
|
||||
.description("histogram")
|
||||
.unit("1pc")
|
||||
.boundaries(ImmutableList.of(10.0, 15.0, 20.0))
|
||||
.tags(ImmutableMap.of("tag1", "value1", "histogram_tag", "default"))
|
||||
.register();
|
||||
}
|
||||
|
||||
@AfterMethod
|
||||
public void maybeShutdownMetrics() {
|
||||
Metrics.shutdown();
|
||||
}
|
||||
|
||||
public void testAddGauge() {
|
||||
Map<TagKey, String> tags = new HashMap<>();
|
||||
tags.put(new TagKey("tag1"), "value1");
|
||||
|
||||
Gauge gauge = new Gauge("metric1", "", "", tags);
|
||||
gauge.update(2);
|
||||
gauge.record();
|
||||
Assert.assertTrue(doubleEqual(gauge.getValue(), 2.0));
|
||||
gauge.unregister();
|
||||
}
|
||||
|
||||
public void testAddGaugeWithTagMap() {
|
||||
Map<String, String> tags = new HashMap<>();
|
||||
tags.put("tag1", "value1");
|
||||
|
||||
Gauge gauge = new Gauge("metric1", "", tags);
|
||||
gauge.update(2);
|
||||
gauge.record();
|
||||
Assert.assertTrue(doubleEqual(gauge.getValue(), 2.0));
|
||||
gauge.unregister();
|
||||
}
|
||||
|
||||
public void testAddCount() {
|
||||
Map<TagKey, String> tags = new HashMap<>();
|
||||
tags.put(new TagKey("tag1"), "value1");
|
||||
tags.put(new TagKey("count_tag"), "default");
|
||||
|
||||
Count count = new Count("metric_count", "counter", "1pc", tags);
|
||||
count.inc(10.0);
|
||||
count.inc(20.0);
|
||||
count.record();
|
||||
Assert.assertTrue(doubleEqual(count.getCount(), 30.0));
|
||||
}
|
||||
|
||||
public void testAddSum() {
|
||||
Map<TagKey, String> tags = new HashMap<>();
|
||||
tags.put(new TagKey("tag1"), "value1");
|
||||
tags.put(new TagKey("sum_tag"), "default");
|
||||
|
||||
Sum sum = new Sum("metric_sum", "sum", "sum", tags);
|
||||
sum.update(10.0);
|
||||
sum.update(20.0);
|
||||
sum.record();
|
||||
Assert.assertTrue(doubleEqual(sum.getSum(), 30.0));
|
||||
}
|
||||
|
||||
public void testAddHistogram() {
|
||||
Map<TagKey, String> tags = new HashMap<>();
|
||||
tags.put(new TagKey("tag1"), "value1");
|
||||
tags.put(new TagKey("histogram_tag"), "default");
|
||||
List<Double> boundaries = new ArrayList<>();
|
||||
boundaries.add(10.0);
|
||||
boundaries.add(12.0);
|
||||
boundaries.add(15.0);
|
||||
Histogram histogram = new Histogram("metric_histogram", "histogram", "1pc", boundaries, tags);
|
||||
for (int i = 1; i <= 200; ++i) {
|
||||
histogram.update(i * 1.0d);
|
||||
}
|
||||
Assert.assertTrue(doubleEqual(200.0d, histogram.getValue()));
|
||||
List<Double> window = histogram.getHistogramWindow();
|
||||
for (int i = 0; i < Histogram.HISTOGRAM_WINDOW_SIZE; ++i) {
|
||||
Assert.assertTrue(doubleEqual(i + 101.0d, window.get(i)));
|
||||
}
|
||||
histogram.record();
|
||||
Assert.assertTrue(doubleEqual(200.0d, histogram.getValue()));
|
||||
Assert.assertEquals(window.size(), 0);
|
||||
}
|
||||
|
||||
public void testRegisterGauge() throws InterruptedException {
|
||||
Gauge gauge = registerGauge();
|
||||
|
||||
gauge.update(2.0);
|
||||
Assert.assertTrue(doubleEqual(gauge.getValue(), 2.0));
|
||||
gauge.update(5.0);
|
||||
Assert.assertTrue(doubleEqual(gauge.getValue(), 5.0));
|
||||
}
|
||||
|
||||
public void testRegisterCount() throws InterruptedException {
|
||||
Count count = registerCount();
|
||||
|
||||
count.inc(10.0);
|
||||
count.inc(20.0);
|
||||
Assert.assertTrue(doubleEqual(count.getCount(), 30.0));
|
||||
count.inc(1.0);
|
||||
count.inc(2.0);
|
||||
Assert.assertTrue(doubleEqual(count.getCount(), 33.0));
|
||||
}
|
||||
|
||||
public void testRegisterSum() throws InterruptedException {
|
||||
Sum sum = registerSum();
|
||||
|
||||
sum.update(10.0);
|
||||
sum.update(20.0);
|
||||
Assert.assertTrue(doubleEqual(sum.getSum(), 30.0));
|
||||
sum.update(1.0);
|
||||
sum.update(2.0);
|
||||
Assert.assertTrue(doubleEqual(sum.getSum(), 33.0));
|
||||
}
|
||||
|
||||
public void testRegisterHistogram() throws InterruptedException {
|
||||
Histogram histogram = registerHistogram();
|
||||
|
||||
for (int i = 1; i <= 200; ++i) {
|
||||
histogram.update(i * 1.0d);
|
||||
}
|
||||
Assert.assertTrue(doubleEqual(histogram.getValue(), 200.0d));
|
||||
List<Double> window = histogram.getHistogramWindow();
|
||||
for (int i = 0; i < Histogram.HISTOGRAM_WINDOW_SIZE; ++i) {
|
||||
Assert.assertTrue(doubleEqual(i + 101.0d, window.get(i)));
|
||||
}
|
||||
Assert.assertTrue(doubleEqual(histogram.getValue(), 200.0d));
|
||||
}
|
||||
|
||||
public void testRegisterGaugeWithConfig() throws InterruptedException {
|
||||
initRayMetrics(2000L, 1, 1000L);
|
||||
Gauge gauge = registerGauge();
|
||||
|
||||
gauge.update(2.0);
|
||||
Assert.assertTrue(doubleEqual(gauge.getValue(), 2.0));
|
||||
gauge.update(5.0);
|
||||
Assert.assertTrue(doubleEqual(gauge.getValue(), 5.0));
|
||||
}
|
||||
|
||||
public void testRegisterCountWithConfig() throws InterruptedException {
|
||||
initRayMetrics(2000L, 1, 1000L);
|
||||
Count count = registerCount();
|
||||
|
||||
count.inc(10.0);
|
||||
count.inc(20.0);
|
||||
Assert.assertTrue(doubleEqual(count.getCount(), 30.0));
|
||||
count.inc(1.0);
|
||||
count.inc(2.0);
|
||||
Assert.assertTrue(doubleEqual(count.getCount(), 33.0));
|
||||
}
|
||||
|
||||
public void testRegisterSumWithConfig() throws InterruptedException {
|
||||
initRayMetrics(2000L, 1, 1000L);
|
||||
Sum sum = registerSum();
|
||||
|
||||
sum.update(10.0);
|
||||
sum.update(20.0);
|
||||
Assert.assertTrue(doubleEqual(sum.getSum(), 30.0));
|
||||
sum.update(1.0);
|
||||
sum.update(2.0);
|
||||
Assert.assertTrue(doubleEqual(sum.getSum(), 33.0));
|
||||
}
|
||||
|
||||
public void testRegisterHistogramWithConfig() throws InterruptedException {
|
||||
initRayMetrics(2000L, 1, 1000L);
|
||||
Histogram histogram = registerHistogram();
|
||||
|
||||
for (int i = 1; i <= 200; ++i) {
|
||||
histogram.update(i * 1.0d);
|
||||
}
|
||||
Assert.assertTrue(doubleEqual(histogram.getValue(), 200.0d));
|
||||
List<Double> window = histogram.getHistogramWindow();
|
||||
for (int i = 0; i < Histogram.HISTOGRAM_WINDOW_SIZE; ++i) {
|
||||
Assert.assertTrue(doubleEqual(i + 101.0d, window.get(i)));
|
||||
}
|
||||
Assert.assertTrue(doubleEqual(histogram.getValue(), 200.0d));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.runtime.util.SystemUtil;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.ProcessBuilder.Redirect;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class MultiDriverTest extends BaseTest {
|
||||
|
||||
private static final int DRIVER_COUNT = 10;
|
||||
private static final int NORMAL_TASK_COUNT_PER_DRIVER = 100;
|
||||
private static final int ACTOR_COUNT_PER_DRIVER = 10;
|
||||
private static final String PID_LIST_PREFIX = "PID: ";
|
||||
|
||||
static int getPid() {
|
||||
return SystemUtil.pid();
|
||||
}
|
||||
|
||||
public static class Actor {
|
||||
|
||||
public int getPid() {
|
||||
return SystemUtil.pid();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
Ray.init();
|
||||
|
||||
List<ObjectRef<Integer>> pidObjectList = new ArrayList<>();
|
||||
// Submit some normal tasks and get the PIDs of workers which execute the tasks.
|
||||
for (int i = 0; i < NORMAL_TASK_COUNT_PER_DRIVER; ++i) {
|
||||
pidObjectList.add(Ray.task(MultiDriverTest::getPid).remote());
|
||||
}
|
||||
// Create some actors and get the PIDs of actors.
|
||||
for (int i = 0; i < ACTOR_COUNT_PER_DRIVER; ++i) {
|
||||
ActorHandle<Actor> actor = Ray.actor(Actor::new).remote();
|
||||
pidObjectList.add(actor.task(Actor::getPid).remote());
|
||||
}
|
||||
Set<Integer> pids = new HashSet<>();
|
||||
for (ObjectRef<Integer> object : pidObjectList) {
|
||||
pids.add(object.get());
|
||||
}
|
||||
// Write pids to stdout
|
||||
System.out.println(
|
||||
PID_LIST_PREFIX + pids.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
||||
}
|
||||
|
||||
public void testMultiDrivers() throws InterruptedException, IOException {
|
||||
// This test case starts some driver processes. Each driver process submits some tasks and
|
||||
// collect the PIDs of the workers used by the driver. The drivers output the PID list
|
||||
// which will be read by the test case itself. The test case will compare the PIDs used by
|
||||
// different drivers and make sure that all the PIDs don't overlap. If overlapped, it means that
|
||||
// tasks owned by different drivers were scheduled to the same worker process, that is, tasks
|
||||
// of different jobs were not correctly isolated during execution.
|
||||
List<Process> drivers = new ArrayList<>();
|
||||
for (int i = 0; i < DRIVER_COUNT; ++i) {
|
||||
drivers.add(startDriver());
|
||||
}
|
||||
|
||||
// Wait for drivers to finish.
|
||||
for (Process driver : drivers) {
|
||||
driver.waitFor();
|
||||
Assert.assertEquals(
|
||||
driver.exitValue(), 0, "The driver exited with code " + driver.exitValue());
|
||||
}
|
||||
|
||||
// Read driver outputs and check for any PID overlap.
|
||||
Set<Integer> pids = new HashSet<>();
|
||||
for (Process driver : drivers) {
|
||||
try (BufferedReader reader =
|
||||
new BufferedReader(new InputStreamReader(driver.getInputStream()))) {
|
||||
String line;
|
||||
int previousSize = pids.size();
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.startsWith(PID_LIST_PREFIX)) {
|
||||
for (String pidString : line.substring(PID_LIST_PREFIX.length()).split(",")) {
|
||||
// Make sure the PIDs don't overlap.
|
||||
Assert.assertTrue(
|
||||
pids.add(Integer.valueOf(pidString)),
|
||||
"Worker process with PID " + line + " is shared by multiple drivers.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
int nowSize = pids.size();
|
||||
Assert.assertTrue(nowSize > previousSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Process startDriver() throws IOException {
|
||||
ProcessBuilder builder = TestUtils.buildDriver(MultiDriverTest.class, null);
|
||||
builder.redirectError(Redirect.INHERIT);
|
||||
return builder.start();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class MultiLanguageClusterTest extends BaseTest {
|
||||
|
||||
public static String echo(String word) {
|
||||
return word;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiLanguageCluster() {
|
||||
ObjectRef<String> obj = Ray.task(MultiLanguageClusterTest::echo, "hello").remote();
|
||||
Assert.assertEquals("hello", obj.get());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.WaitResult;
|
||||
import io.ray.api.id.ActorId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test
|
||||
public class MultiThreadingTest extends BaseTest {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MultiThreadingTest.class);
|
||||
|
||||
private static final int LOOP_COUNTER = 100;
|
||||
private static final int NUM_THREADS = 20;
|
||||
|
||||
static Integer echo(int num) {
|
||||
return num;
|
||||
}
|
||||
|
||||
public static class Echo {
|
||||
|
||||
public Integer echo(int num) {
|
||||
return num;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ActorIdTester {
|
||||
|
||||
private final ActorId actorId;
|
||||
|
||||
public ActorIdTester() {
|
||||
actorId = Ray.getRuntimeContext().getCurrentActorId();
|
||||
Assert.assertNotEquals(actorId, ActorId.NIL);
|
||||
}
|
||||
|
||||
public ActorId getCurrentActorId() throws Exception {
|
||||
final Object[] result = new Object[1];
|
||||
Thread thread =
|
||||
new Thread(
|
||||
() -> {
|
||||
try {
|
||||
result[0] = Ray.getRuntimeContext().getCurrentActorId();
|
||||
} catch (Exception e) {
|
||||
result[0] = e;
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
thread.join();
|
||||
if (result[0] instanceof Exception) {
|
||||
throw (Exception) result[0];
|
||||
}
|
||||
Assert.assertEquals(result[0], actorId);
|
||||
return (ActorId) result[0];
|
||||
}
|
||||
}
|
||||
|
||||
static String testMultiThreading() {
|
||||
Random random = new Random();
|
||||
// Test calling normal functions.
|
||||
runTestCaseInMultipleThreads(
|
||||
() -> {
|
||||
int arg = random.nextInt();
|
||||
ObjectRef<Integer> obj = Ray.task(MultiThreadingTest::echo, arg).remote();
|
||||
Assert.assertEquals(arg, (int) obj.get());
|
||||
},
|
||||
LOOP_COUNTER);
|
||||
|
||||
// Test calling actors.
|
||||
ActorHandle<Echo> echoActor = Ray.actor(Echo::new).remote();
|
||||
runTestCaseInMultipleThreads(
|
||||
() -> {
|
||||
int arg = random.nextInt();
|
||||
ObjectRef<Integer> obj = echoActor.task(Echo::echo, arg).remote();
|
||||
Assert.assertEquals(arg, (int) obj.get());
|
||||
},
|
||||
LOOP_COUNTER);
|
||||
|
||||
// Test creating multi actors
|
||||
runTestCaseInMultipleThreads(
|
||||
() -> {
|
||||
int arg = random.nextInt();
|
||||
ActorHandle<Echo> echoActor1 = Ray.actor(Echo::new).remote();
|
||||
try {
|
||||
// Sleep a while to test the case that another actor is created before submitting
|
||||
// tasks to this actor.
|
||||
TimeUnit.MILLISECONDS.sleep(10);
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.warn("Got exception while sleeping.", e);
|
||||
}
|
||||
ObjectRef<Integer> obj = echoActor1.task(Echo::echo, arg).remote();
|
||||
Assert.assertEquals(arg, (int) obj.get());
|
||||
},
|
||||
1);
|
||||
|
||||
// Test put and get.
|
||||
runTestCaseInMultipleThreads(
|
||||
() -> {
|
||||
int arg = random.nextInt();
|
||||
ObjectRef<Integer> obj = Ray.put(arg);
|
||||
Assert.assertEquals(arg, (int) obj.get());
|
||||
},
|
||||
LOOP_COUNTER);
|
||||
|
||||
TestUtils.warmUpCluster();
|
||||
// Test wait for one object in multi threads.
|
||||
ObjectRef<Integer> obj = Ray.task(MultiThreadingTest::echo, 100).remote();
|
||||
runTestCaseInMultipleThreads(
|
||||
() -> {
|
||||
WaitResult<Integer> result = Ray.wait(ImmutableList.of(obj), 1, 1000);
|
||||
Assert.assertEquals(1, result.getReady().size());
|
||||
},
|
||||
1);
|
||||
|
||||
return "ok";
|
||||
}
|
||||
|
||||
public void testInDriver() {
|
||||
testMultiThreading();
|
||||
}
|
||||
|
||||
// Local mode doesn't have real workers.
|
||||
@Test(groups = {"cluster"})
|
||||
public void testInWorker() {
|
||||
ObjectRef<String> obj = Ray.task(MultiThreadingTest::testMultiThreading).remote();
|
||||
Assert.assertEquals("ok", obj.get());
|
||||
}
|
||||
|
||||
/// SINGLE_PROCESS mode doesn't support this API.
|
||||
@Test(groups = {"cluster"})
|
||||
public void testGetCurrentActorId() {
|
||||
ActorHandle<ActorIdTester> actorIdTester = Ray.actor(ActorIdTester::new).remote();
|
||||
ActorId actorId = actorIdTester.task(ActorIdTester::getCurrentActorId).remote().get();
|
||||
Assert.assertEquals(actorId, actorIdTester.getId());
|
||||
}
|
||||
|
||||
/** Call this method each time to avoid hitting the cache in {@link ObjectRef#get()}. */
|
||||
static Runnable[] generateRunnables() {
|
||||
final ObjectRef<Integer> fooObject = Ray.put(1);
|
||||
final ActorHandle<Echo> fooActor = Ray.actor(Echo::new).remote();
|
||||
return new Runnable[] {
|
||||
() -> Ray.put(1),
|
||||
() -> Ray.get(ImmutableList.of(fooObject)),
|
||||
fooObject::get,
|
||||
() -> Ray.wait(ImmutableList.of(fooObject)),
|
||||
Ray::getRuntimeContext,
|
||||
() -> Ray.task(MultiThreadingTest::echo, 1).remote(),
|
||||
() -> Ray.actor(Echo::new).remote(),
|
||||
() -> fooActor.task(Echo::echo, 1).remote(),
|
||||
};
|
||||
}
|
||||
|
||||
private static void runTestCaseInMultipleThreads(Runnable testCase, int numRepeats) {
|
||||
ExecutorService service = Executors.newFixedThreadPool(NUM_THREADS);
|
||||
|
||||
try {
|
||||
List<Future<String>> futures = new ArrayList<>();
|
||||
for (int i = 0; i < NUM_THREADS; i++) {
|
||||
Callable<String> task =
|
||||
() -> {
|
||||
for (int j = 0; j < numRepeats; j++) {
|
||||
TimeUnit.MILLISECONDS.sleep(1);
|
||||
testCase.run();
|
||||
}
|
||||
return "ok";
|
||||
};
|
||||
futures.add(service.submit(task));
|
||||
}
|
||||
for (Future<String> future : futures) {
|
||||
try {
|
||||
Assert.assertEquals(future.get(), "ok");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Test case failed.", e);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
service.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import java.util.Optional;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class NamedActorTest extends BaseTest {
|
||||
|
||||
public static class Counter {
|
||||
|
||||
private int value = 0;
|
||||
|
||||
public int increment() {
|
||||
this.value += 1;
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
void initNamespace() {
|
||||
System.setProperty("ray.job.namespace", "named_actor_test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNamedActor() {
|
||||
String name = "named-actor-counter";
|
||||
// Create an actor.
|
||||
ActorHandle<Counter> actor = Ray.actor(Counter::new).setName(name).remote();
|
||||
Assert.assertEquals(actor.task(Counter::increment).remote().get(), Integer.valueOf(1));
|
||||
// Get the named actor.
|
||||
Assert.assertTrue(Ray.getActor(name).isPresent());
|
||||
Optional<ActorHandle<Counter>> namedActor = Ray.getActor(name);
|
||||
Assert.assertTrue(namedActor.isPresent());
|
||||
// Verify that this handle is correct.
|
||||
Assert.assertEquals(
|
||||
namedActor.get().task(Counter::increment).remote().get(), Integer.valueOf(2));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testActorDuplicatedName() {
|
||||
String name = "named-actor-counter";
|
||||
// Create an actor.
|
||||
ActorHandle<Counter> actor = Ray.actor(Counter::new).setName(name).remote();
|
||||
// Ensure async actor creation is finished.
|
||||
Assert.assertEquals(actor.task(Counter::increment).remote().get(), Integer.valueOf(1));
|
||||
// Registering with the same name should fail.
|
||||
Ray.actor(Counter::new).setName(name).remote();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNonExistingNamedActor() {
|
||||
Assert.assertTrue(!Ray.getActor("non_existing_actor").isPresent());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.options.ActorLifetime;
|
||||
import java.io.IOException;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = "cluster")
|
||||
public class NamespaceTest {
|
||||
|
||||
private static class A {
|
||||
public String hello() {
|
||||
return "hello";
|
||||
}
|
||||
}
|
||||
|
||||
/// This case tests that actor cannot be accessed in different namespaces.
|
||||
public void testIsolationBetweenNamespaces() throws IOException, InterruptedException {
|
||||
System.setProperty("ray.job.namespace", "test2");
|
||||
testIsolation(
|
||||
MainClassForNamespaceTest.class,
|
||||
() ->
|
||||
Assert.assertThrows(
|
||||
NoSuchElementException.class,
|
||||
() -> {
|
||||
Ray.getActor("a").get();
|
||||
}));
|
||||
}
|
||||
|
||||
/// This case tests that actor can be accessed between different jobs but in the same namespace.
|
||||
public void testIsolationInTheSameNamespaces() throws IOException, InterruptedException {
|
||||
System.setProperty("ray.job.namespace", "test1");
|
||||
testIsolation(
|
||||
MainClassForNamespaceTest.class,
|
||||
() -> {
|
||||
ActorHandle<A> a = (ActorHandle<A>) Ray.getActor("a").get();
|
||||
Assert.assertEquals("hello", a.task(A::hello).remote().get());
|
||||
});
|
||||
}
|
||||
|
||||
public void testIsolationBetweenAnonymousNamespaces() throws IOException, InterruptedException {
|
||||
NamespaceTest.testIsolation(
|
||||
MainClassForAnonymousNamespaceTest.class,
|
||||
() ->
|
||||
Assert.assertThrows(
|
||||
NoSuchElementException.class,
|
||||
() -> {
|
||||
Ray.getActor("a").get();
|
||||
}));
|
||||
}
|
||||
|
||||
private static String getNamespace() {
|
||||
return Ray.getRuntimeContext().getNamespace();
|
||||
}
|
||||
|
||||
private static class GetNamespaceActor {
|
||||
public String getNamespace() {
|
||||
return Ray.getRuntimeContext().getNamespace();
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetNamespace() {
|
||||
final String thisNamespace = "test_get_current_namespace";
|
||||
System.setProperty("ray.job.namespace", thisNamespace);
|
||||
try {
|
||||
Ray.init();
|
||||
/// Test in driver.
|
||||
Assert.assertEquals(thisNamespace, Ray.getRuntimeContext().getNamespace());
|
||||
/// Test in task.
|
||||
Assert.assertEquals(thisNamespace, Ray.task(NamespaceTest::getNamespace).remote().get());
|
||||
/// Test in actor.
|
||||
ActorHandle<GetNamespaceActor> a = Ray.actor(GetNamespaceActor::new).remote();
|
||||
Assert.assertEquals(thisNamespace, a.task(GetNamespaceActor::getNamespace).remote().get());
|
||||
} finally {
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MainClassForNamespaceTest {
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
System.setProperty("ray.job.namespace", "test1");
|
||||
startDriver();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MainClassForAnonymousNamespaceTest {
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
startDriver();
|
||||
}
|
||||
}
|
||||
|
||||
private static void startDriver() throws InterruptedException {
|
||||
Ray.init();
|
||||
ActorHandle<A> a = Ray.actor(A::new).setLifetime(ActorLifetime.DETACHED).setName("a").remote();
|
||||
Assert.assertEquals("hello", a.task(A::hello).remote().get());
|
||||
/// Because we don't support long running job yet, so sleep to don't destroy
|
||||
/// it for a while. Otherwise the actor created in this job will be destroyed
|
||||
/// as well.
|
||||
TimeUnit.SECONDS.sleep(10);
|
||||
Ray.shutdown();
|
||||
}
|
||||
|
||||
private static void testIsolation(Class<?> driverClass, Runnable runnable)
|
||||
throws IOException, InterruptedException {
|
||||
Process driver = null;
|
||||
try {
|
||||
Ray.init();
|
||||
ProcessBuilder builder = TestUtils.buildDriver(driverClass, null);
|
||||
builder.redirectError(ProcessBuilder.Redirect.INHERIT);
|
||||
driver = builder.start();
|
||||
TestUtils.waitForCondition(() -> Ray.getActor("a", "test1").isPresent(), 10000);
|
||||
// Wait for driver to start.
|
||||
driver.waitFor(10, TimeUnit.SECONDS);
|
||||
runnable.run();
|
||||
} finally {
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public void testSpecifyNamespaceForActor() {
|
||||
System.setProperty("ray.job.namespace", "namespace1");
|
||||
try {
|
||||
Ray.init();
|
||||
ActorHandle<GetNamespaceActor> actor =
|
||||
Ray.actor(GetNamespaceActor::new).setName("a", "namespace2").remote();
|
||||
Assert.assertFalse(Ray.getActor("a").isPresent());
|
||||
} finally {
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.Ray;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.testng.Assert;
|
||||
import org.testng.SkipException;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class NodeIpTest extends BaseTest {
|
||||
|
||||
private static final String NODE_IP = "127.0.0.2";
|
||||
|
||||
@BeforeClass
|
||||
public void setUp() {
|
||||
if (SystemUtils.IS_OS_MAC) {
|
||||
throw new SkipException("Skip NodeIpTest on Mac OS");
|
||||
}
|
||||
System.setProperty("ray.head-args.0", "--node-ip-address=127.0.0.2");
|
||||
System.setProperty("ray.node-ip", "127.0.0.2");
|
||||
}
|
||||
|
||||
static String getNodeIp() {
|
||||
return TestUtils.getRuntime().getRayConfig().nodeIp;
|
||||
}
|
||||
|
||||
public void testNodeIp() {
|
||||
// this is on the driver node, and it should be equal with ray.node-ip
|
||||
String nodeIP = TestUtils.getRuntime().getRayConfig().nodeIp;
|
||||
Assert.assertEquals(nodeIP, NODE_IP);
|
||||
|
||||
// this is on the worker node, and it should be equal with node-ip-address
|
||||
nodeIP = Ray.task(NodeIpTest::getNodeIp).remote().get();
|
||||
Assert.assertEquals(nodeIP, NODE_IP);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.runtimecontext.NodeInfo;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = "cluster")
|
||||
public class NodeLabelSchedulingTest {
|
||||
public void testEmptyNodeLabels() {
|
||||
try {
|
||||
Ray.init();
|
||||
List<NodeInfo> nodeInfos = Ray.getRuntimeContext().getAllNodeInfo();
|
||||
Assert.assertTrue(nodeInfos.size() == 1);
|
||||
Map<String, String> labels = new HashMap<>();
|
||||
labels.put("ray.io/node-id", nodeInfos.get(0).nodeId.toString());
|
||||
Assert.assertEquals(nodeInfos.get(0).labels, labels);
|
||||
} finally {
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public void testSetNodeLabels() {
|
||||
System.setProperty("ray.head-args.0", "--labels={\"gpu_type\":\"A100\",\"azone\":\"azone-1\"}");
|
||||
try {
|
||||
Ray.init();
|
||||
List<NodeInfo> nodeInfos = Ray.getRuntimeContext().getAllNodeInfo();
|
||||
Assert.assertTrue(nodeInfos.size() == 1);
|
||||
Map<String, String> labels = new HashMap<>();
|
||||
labels.put("ray.io/node-id", nodeInfos.get(0).nodeId.toString());
|
||||
labels.put("gpu_type", "A100");
|
||||
labels.put("azone", "azone-1");
|
||||
Assert.assertEquals(nodeInfos.get(0).labels, labels);
|
||||
} finally {
|
||||
System.clearProperty("ray.head-args.0");
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class ObjectRefTransferTest extends BaseTest {
|
||||
|
||||
@Test
|
||||
public void testObjectTransfer() {
|
||||
ObjectRef<String> objectRef = Ray.put("test");
|
||||
List<ObjectRef<String>> data = new ArrayList<>();
|
||||
data.add(objectRef);
|
||||
|
||||
ActorHandle<RemoteActor> handle = Ray.actor(RemoteActor::new).remote();
|
||||
String result = handle.task(RemoteActor::get, data).remote().get();
|
||||
Assert.assertEquals(result, "test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedObjectId() {
|
||||
ObjectRef<String> inner = Ray.put("inner");
|
||||
ObjectRef<ObjectRef<String>> outer = Ray.put(inner);
|
||||
List<ObjectRef<ObjectRef<String>>> data = new ArrayList<>();
|
||||
data.add(outer);
|
||||
|
||||
ActorHandle<RemoteActor> handle = Ray.actor(RemoteActor::new).remote();
|
||||
String result = handle.task(RemoteActor::getNested, data).remote().get();
|
||||
Assert.assertEquals(result, "inner");
|
||||
}
|
||||
|
||||
public static class RemoteActor {
|
||||
public String get(List<ObjectRef<String>> value) {
|
||||
return Ray.get(value.get(0));
|
||||
}
|
||||
|
||||
public String getNested(List<ObjectRef<ObjectRef<String>>> value) {
|
||||
return Ray.get(value.get(0).get());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/** Test putting and getting objects. */
|
||||
public class ObjectStoreTest extends BaseTest {
|
||||
|
||||
@Test
|
||||
public void testPutAndGet() {
|
||||
{
|
||||
ObjectRef<Integer> obj = Ray.put(1);
|
||||
Assert.assertEquals(1, (int) obj.get());
|
||||
}
|
||||
|
||||
{
|
||||
String s = null;
|
||||
ObjectRef<String> obj = Ray.put(s);
|
||||
Assert.assertNull(obj.get());
|
||||
}
|
||||
|
||||
{
|
||||
List<List<String>> l = ImmutableList.of(ImmutableList.of("abc"));
|
||||
ObjectRef<List<List<String>>> obj = Ray.put(l);
|
||||
Assert.assertEquals(obj.get(), l);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMultipleObjects() {
|
||||
List<Integer> ints = ImmutableList.of(1, 2, 3, 4, 5);
|
||||
List<ObjectRef<Integer>> refs = ints.stream().map(Ray::put).collect(Collectors.toList());
|
||||
Assert.assertEquals(ints, Ray.get(refs));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.exception.RayActorException;
|
||||
import io.ray.api.parallelactor.*;
|
||||
import io.ray.runtime.util.SystemUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = "cluster")
|
||||
public class ParallelActorTest extends BaseTest {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ParallelActorTest.class);
|
||||
|
||||
private static class A {
|
||||
private int value = 0;
|
||||
|
||||
public int incr(int delta) {
|
||||
value += delta;
|
||||
return value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
public int getPid() {
|
||||
// TODO(qwang): We should fix this once Serialization2.0 finished.
|
||||
return 1000000 + SystemUtil.pid();
|
||||
}
|
||||
|
||||
public int getThreadId() {
|
||||
return 1000000 + (int) Thread.currentThread().getId();
|
||||
}
|
||||
}
|
||||
|
||||
public void testBasic() {
|
||||
ParallelActorHandle<A> actor = ParallelActor.actor(A::new).setParallelism(10).remote();
|
||||
{
|
||||
// stateless tests
|
||||
ParallelActorInstance<A> instance = actor.getInstance(2);
|
||||
|
||||
Preconditions.checkNotNull(instance);
|
||||
int res = instance.task(A::add, 100000, 200000).remote().get(); // Executed in instance 2
|
||||
Assert.assertEquals(res, 300000);
|
||||
|
||||
instance = actor.getInstance(3);
|
||||
Preconditions.checkNotNull(instance);
|
||||
res = instance.task(A::add, 100000, 200000).remote().get(); // Executed in instance 2
|
||||
Assert.assertEquals(res, 300000);
|
||||
}
|
||||
|
||||
{
|
||||
// stateful tests
|
||||
ParallelActorInstance<A> instance = actor.getInstance(2);
|
||||
|
||||
Preconditions.checkNotNull(instance);
|
||||
int res = instance.task(A::incr, 1000000).remote().get(); // Executed in instance 2
|
||||
Assert.assertEquals(res, 1000000);
|
||||
|
||||
instance = actor.getInstance(2);
|
||||
Preconditions.checkNotNull(instance);
|
||||
res = instance.task(A::incr, 2000000).remote().get(); // Executed in instance 2
|
||||
Assert.assertEquals(res, 3000000);
|
||||
}
|
||||
|
||||
{
|
||||
// Test they are in the same process.
|
||||
ParallelActorInstance<A> instance = actor.getInstance(0);
|
||||
Preconditions.checkNotNull(instance);
|
||||
int pid1 = instance.task(A::getPid).remote().get();
|
||||
|
||||
instance = actor.getInstance(1);
|
||||
Preconditions.checkNotNull(instance);
|
||||
int pid2 = instance.task(A::getPid).remote().get();
|
||||
Assert.assertEquals(pid1, pid2);
|
||||
}
|
||||
|
||||
{
|
||||
// Test they are in different threads.
|
||||
ParallelActorInstance<A> instance = actor.getInstance(4);
|
||||
Preconditions.checkNotNull(instance);
|
||||
int thread1 = instance.task(A::getThreadId).remote().get();
|
||||
|
||||
instance = actor.getInstance(5);
|
||||
Preconditions.checkNotNull(instance);
|
||||
int thread2 = instance.task(A::getThreadId).remote().get();
|
||||
Assert.assertNotEquals(thread1, thread2);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean passParallelActor(ParallelActorHandle<A> parallelActorHandle) {
|
||||
ObjectRef<Integer> obj0 = parallelActorHandle.getInstance(0).task(A::incr, 1000000).remote();
|
||||
ObjectRef<Integer> obj1 = parallelActorHandle.getInstance(1).task(A::incr, 2000000).remote();
|
||||
Assert.assertEquals(2000000, (int) obj0.get());
|
||||
Assert.assertEquals(4000000, (int) obj1.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
public void testPassParallelActorHandle() {
|
||||
ParallelActorHandle<A> actor = ParallelActor.actor(A::new).setParallelism(10).remote();
|
||||
ObjectRef<Integer> obj0 = actor.getInstance(0).task(A::incr, 1000000).remote();
|
||||
ObjectRef<Integer> obj1 = actor.getInstance(1).task(A::incr, 2000000).remote();
|
||||
Assert.assertEquals(1000000, (int) obj0.get());
|
||||
Assert.assertEquals(2000000, (int) obj1.get());
|
||||
Assert.assertTrue(Ray.task(ParallelActorTest::passParallelActor, actor).remote().get());
|
||||
}
|
||||
|
||||
public void testKillParallelActor() {
|
||||
ParallelActorHandle<A> actor = ParallelActor.actor(A::new).setParallelism(10).remote();
|
||||
ObjectRef<Integer> obj0 = actor.getInstance(0).task(A::incr, 1000000).remote();
|
||||
Assert.assertEquals(1000000, (int) obj0.get());
|
||||
|
||||
ActorHandle<?> handle = actor.getHandle();
|
||||
handle.kill(true);
|
||||
final ObjectRef<Integer> obj1 = actor.getInstance(0).task(A::incr, 1000000).remote();
|
||||
Assert.expectThrows(RayActorException.class, obj1::get);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.PlacementGroups;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.WaitResult;
|
||||
import io.ray.api.exception.RayException;
|
||||
import io.ray.api.id.ActorId;
|
||||
import io.ray.api.placementgroup.PlacementGroup;
|
||||
import io.ray.api.placementgroup.PlacementGroupState;
|
||||
import io.ray.api.placementgroup.PlacementStrategy;
|
||||
import java.util.List;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* TODO: Currently, Java doesn't support multi-node tests so we can't test all strategy temporarily.
|
||||
*/
|
||||
@Test
|
||||
public class PlacementGroupTest extends BaseTest {
|
||||
|
||||
public static class Counter {
|
||||
|
||||
private int value;
|
||||
|
||||
public Counter(int initValue) {
|
||||
this.value = initValue;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static String ping() {
|
||||
return "pong";
|
||||
}
|
||||
}
|
||||
|
||||
// This test just creates a placement group with one bundle.
|
||||
// It's not comprehensive to test all placement group test cases.
|
||||
public void testCreateAndCallActor() {
|
||||
PlacementGroup placementGroup =
|
||||
PlacementGroupTestUtils.createSpecifiedSimpleGroup(
|
||||
"CPU", 2, PlacementStrategy.PACK, 1.0, false);
|
||||
Assert.assertTrue(placementGroup.wait(60));
|
||||
Assert.assertEquals(placementGroup.getName(), "unnamed_group");
|
||||
|
||||
// Test creating an actor from a constructor.
|
||||
ActorHandle<Counter> firstActor =
|
||||
Ray.actor(Counter::new, 1)
|
||||
.setResource("CPU", 1.0)
|
||||
.setPlacementGroup(placementGroup, 0)
|
||||
.remote();
|
||||
Assert.assertNotEquals(firstActor.getId(), ActorId.NIL);
|
||||
|
||||
// Test calling an actor.
|
||||
Assert.assertEquals(firstActor.task(Counter::getValue).remote().get(), Integer.valueOf(1));
|
||||
|
||||
// Test creating an actor without specifying which bundle to use.
|
||||
ActorHandle<Counter> secondActor =
|
||||
Ray.actor(Counter::new, 1)
|
||||
.setResource("CPU", 1.0)
|
||||
.setPlacementGroup(placementGroup)
|
||||
.remote();
|
||||
Assert.assertNotEquals(secondActor.getId(), ActorId.NIL);
|
||||
|
||||
// Test calling an actor.
|
||||
Assert.assertEquals(secondActor.task(Counter::getValue).remote().get(), Integer.valueOf(1));
|
||||
}
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public void testGetPlacementGroup() {
|
||||
PlacementGroup firstPlacementGroup =
|
||||
PlacementGroupTestUtils.createNameSpecifiedSimpleGroup(
|
||||
"CPU", 1, PlacementStrategy.PACK, 1.0, "first_placement_group");
|
||||
|
||||
PlacementGroup secondPlacementGroup =
|
||||
PlacementGroupTestUtils.createNameSpecifiedSimpleGroup(
|
||||
"CPU", 1, PlacementStrategy.PACK, 1.0, "second_placement_group");
|
||||
Assert.assertTrue(firstPlacementGroup.wait(60));
|
||||
Assert.assertTrue(secondPlacementGroup.wait(60));
|
||||
|
||||
PlacementGroup firstPlacementGroupRes =
|
||||
PlacementGroups.getPlacementGroup((firstPlacementGroup).getId());
|
||||
PlacementGroup secondPlacementGroupRes =
|
||||
PlacementGroups.getPlacementGroup((secondPlacementGroup).getId());
|
||||
|
||||
Assert.assertNotNull(firstPlacementGroupRes);
|
||||
Assert.assertNotNull(secondPlacementGroupRes);
|
||||
|
||||
Assert.assertEquals(firstPlacementGroup.getId(), firstPlacementGroupRes.getId());
|
||||
Assert.assertEquals(firstPlacementGroupRes.getBundles().size(), 1);
|
||||
Assert.assertEquals(firstPlacementGroupRes.getStrategy(), PlacementStrategy.PACK);
|
||||
|
||||
List<PlacementGroup> allPlacementGroup = PlacementGroups.getAllPlacementGroups();
|
||||
Assert.assertEquals(allPlacementGroup.size(), 2);
|
||||
|
||||
PlacementGroup placementGroupRes = allPlacementGroup.get(0);
|
||||
Assert.assertNotNull(placementGroupRes.getId());
|
||||
PlacementGroup expectPlacementGroup =
|
||||
placementGroupRes.getId().equals(firstPlacementGroup.getId())
|
||||
? firstPlacementGroup
|
||||
: secondPlacementGroup;
|
||||
|
||||
Assert.assertEquals(
|
||||
placementGroupRes.getBundles().size(), expectPlacementGroup.getBundles().size());
|
||||
Assert.assertEquals(placementGroupRes.getStrategy(), expectPlacementGroup.getStrategy());
|
||||
}
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public void testRemovePlacementGroup() {
|
||||
PlacementGroup firstPlacementGroup =
|
||||
PlacementGroupTestUtils.createNameSpecifiedSimpleGroup(
|
||||
"CPU", 1, PlacementStrategy.PACK, 1.0, "first_placement_group");
|
||||
|
||||
PlacementGroup secondPlacementGroup =
|
||||
PlacementGroupTestUtils.createNameSpecifiedSimpleGroup(
|
||||
"CPU", 1, PlacementStrategy.PACK, 1.0, "second_placement_group");
|
||||
Assert.assertTrue(firstPlacementGroup.wait(60));
|
||||
Assert.assertTrue(secondPlacementGroup.wait(60));
|
||||
|
||||
List<PlacementGroup> allPlacementGroup = PlacementGroups.getAllPlacementGroups();
|
||||
Assert.assertEquals(allPlacementGroup.size(), 2);
|
||||
|
||||
PlacementGroups.removePlacementGroup(secondPlacementGroup.getId());
|
||||
|
||||
PlacementGroup removedPlacementGroup =
|
||||
PlacementGroups.getPlacementGroup((secondPlacementGroup).getId());
|
||||
Assert.assertEquals(removedPlacementGroup.getState(), PlacementGroupState.REMOVED);
|
||||
|
||||
// Wait for placement group after it is removed.
|
||||
int exceptionCount = 0;
|
||||
try {
|
||||
removedPlacementGroup.wait(10);
|
||||
} catch (RayException e) {
|
||||
++exceptionCount;
|
||||
}
|
||||
Assert.assertEquals(exceptionCount, 1);
|
||||
}
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public void testCheckBundleIndex() {
|
||||
PlacementGroup placementGroup = PlacementGroupTestUtils.createSimpleGroup();
|
||||
Assert.assertTrue(placementGroup.wait(60));
|
||||
|
||||
int exceptionCount = 0;
|
||||
try {
|
||||
Ray.actor(Counter::new, 1).setPlacementGroup(placementGroup, 1).remote();
|
||||
} catch (IllegalArgumentException e) {
|
||||
++exceptionCount;
|
||||
}
|
||||
Assert.assertEquals(exceptionCount, 1);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = {IllegalArgumentException.class})
|
||||
public void testBundleSizeValidCheckWhenCreate() {
|
||||
PlacementGroupTestUtils.createBundleSizeInvalidGroup();
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = {IllegalArgumentException.class})
|
||||
public void testBundleResourceValidCheckWhenCreate() {
|
||||
PlacementGroupTestUtils.createBundleResourceInvalidGroup();
|
||||
}
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public void testNamedPlacementGroup() {
|
||||
// Test Non-Global placement group.
|
||||
String pgName = "named_placement_group";
|
||||
PlacementGroup firstPlacementGroup =
|
||||
PlacementGroupTestUtils.createNameSpecifiedSimpleGroup(
|
||||
"CPU", 1, PlacementStrategy.PACK, 1.0, pgName);
|
||||
Assert.assertTrue(firstPlacementGroup.wait(60));
|
||||
// Make sure we can get it by name successfully.
|
||||
PlacementGroup placementGroup = PlacementGroups.getPlacementGroup(pgName);
|
||||
Assert.assertNotNull(placementGroup);
|
||||
Assert.assertEquals(placementGroup.getBundles().size(), 1);
|
||||
}
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public void testCreatePlacementGroupWithSameName() {
|
||||
String pgName = "named_placement_group";
|
||||
PlacementGroup firstPlacementGroup =
|
||||
PlacementGroupTestUtils.createNameSpecifiedSimpleGroup(
|
||||
"CPU", 1, PlacementStrategy.PACK, 1.0, pgName);
|
||||
Assert.assertTrue(firstPlacementGroup.wait(60));
|
||||
int exceptionCount = 0;
|
||||
try {
|
||||
PlacementGroupTestUtils.createNameSpecifiedSimpleGroup(
|
||||
"CPU", 1, PlacementStrategy.PACK, 1.0, pgName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
++exceptionCount;
|
||||
}
|
||||
Assert.assertEquals(exceptionCount, 1);
|
||||
}
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public void testPlacementGroupForNormalTask() {
|
||||
// Create a placement group with non-exist resources.
|
||||
String pgName = "named_placement_group";
|
||||
PlacementGroup nonExistPlacementGroup =
|
||||
PlacementGroupTestUtils.createNameSpecifiedSimpleGroup(
|
||||
"non-exist-resource", 1, PlacementStrategy.PACK, 1.0, pgName);
|
||||
|
||||
// Make sure its creation will failed.
|
||||
Assert.assertFalse(nonExistPlacementGroup.wait(60));
|
||||
|
||||
// Submit a normal task that required a non-exist placement group resources and make sure its
|
||||
// scheduling will timeout.
|
||||
ObjectRef<String> obj =
|
||||
Ray.task(Counter::ping)
|
||||
.setPlacementGroup(nonExistPlacementGroup, 0)
|
||||
.setResource("CPU", 1.0)
|
||||
.remote();
|
||||
|
||||
List<ObjectRef<String>> waitList = ImmutableList.of(obj);
|
||||
WaitResult<String> waitResult = Ray.wait(waitList, 1, 30 * 1000);
|
||||
Assert.assertEquals(1, waitResult.getUnready().size());
|
||||
|
||||
// Create a placement group and make sure its creation will successful.
|
||||
PlacementGroup placementGroup = PlacementGroupTestUtils.createSimpleGroup();
|
||||
Assert.assertTrue(placementGroup.wait(60));
|
||||
|
||||
// Submit a normal task that required a exist placement group resources and make sure its
|
||||
// scheduling will successful.
|
||||
Assert.assertEquals(
|
||||
Ray.task(Counter::ping)
|
||||
.setPlacementGroup(placementGroup, 0)
|
||||
.setResource("CPU", 1.0)
|
||||
.remote()
|
||||
.get(),
|
||||
"pong");
|
||||
|
||||
// Submit a normal task without specifying which bundle to use.
|
||||
Assert.assertEquals(
|
||||
Ray.task(Counter::ping)
|
||||
.setPlacementGroup(placementGroup)
|
||||
.setResource("CPU", 1.0)
|
||||
.remote()
|
||||
.get(),
|
||||
"pong");
|
||||
|
||||
// Make sure it will not affect the previous normal task.
|
||||
Assert.assertEquals(Ray.task(Counter::ping).remote().get(), "pong");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.PlacementGroups;
|
||||
import io.ray.api.options.PlacementGroupCreationOptions;
|
||||
import io.ray.api.placementgroup.PlacementGroup;
|
||||
import io.ray.api.placementgroup.PlacementStrategy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** A utils class for placement group test. */
|
||||
public class PlacementGroupTestUtils {
|
||||
|
||||
public static PlacementGroup createNameSpecifiedSimpleGroup(
|
||||
String resourceName,
|
||||
int bundleSize,
|
||||
PlacementStrategy strategy,
|
||||
Double resourceSize,
|
||||
String groupName) {
|
||||
List<Map<String, Double>> bundles = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < bundleSize; i++) {
|
||||
Map<String, Double> bundle = new HashMap<>();
|
||||
bundle.put(resourceName, resourceSize);
|
||||
bundles.add(bundle);
|
||||
}
|
||||
PlacementGroupCreationOptions.Builder builder =
|
||||
new PlacementGroupCreationOptions.Builder().setBundles(bundles).setStrategy(strategy);
|
||||
builder.setName(groupName);
|
||||
|
||||
return PlacementGroups.createPlacementGroup(builder.build());
|
||||
}
|
||||
|
||||
public static PlacementGroup createSpecifiedSimpleGroup(
|
||||
String resourceName,
|
||||
int bundleSize,
|
||||
PlacementStrategy strategy,
|
||||
Double resourceSize,
|
||||
boolean isGlobal) {
|
||||
return createNameSpecifiedSimpleGroup(
|
||||
resourceName, bundleSize, strategy, resourceSize, "unnamed_group");
|
||||
}
|
||||
|
||||
public static PlacementGroup createSimpleGroup() {
|
||||
return createSpecifiedSimpleGroup("CPU", 1, PlacementStrategy.PACK, 1.0, false);
|
||||
}
|
||||
|
||||
public static void createBundleSizeInvalidGroup() {
|
||||
createSpecifiedSimpleGroup("CPU", 0, PlacementStrategy.PACK, 1.0, false);
|
||||
}
|
||||
|
||||
public static void createBundleResourceInvalidGroup() {
|
||||
createSpecifiedSimpleGroup("CPU", 1, PlacementStrategy.PACK, 0.0, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.PlacementGroups;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.id.ActorId;
|
||||
import io.ray.api.options.PlacementGroupCreationOptions;
|
||||
import io.ray.api.placementgroup.PlacementGroup;
|
||||
import io.ray.api.placementgroup.PlacementStrategy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = "cluster")
|
||||
public class PlacementGroupWithResourcesTest extends BaseTest {
|
||||
|
||||
@BeforeClass
|
||||
public void setUp() {
|
||||
System.setProperty("ray.head-args.0", "--num-cpus=1");
|
||||
// 1GB
|
||||
System.setProperty("ray.head-args.1", "--memory=1073741824");
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public void tearDown() {
|
||||
System.clearProperty("ray.head-args.0");
|
||||
System.clearProperty("ray.head-args.1");
|
||||
}
|
||||
|
||||
public static int simpleFunction() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static class Counter {
|
||||
|
||||
private int value;
|
||||
|
||||
public Counter(int initValue) {
|
||||
this.value = initValue;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static String ping() {
|
||||
return "pong";
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActorNoResources() {
|
||||
List<Map<String, Double>> bundles = new ArrayList<>();
|
||||
Map<String, Double> bundle = new HashMap<>();
|
||||
bundle.put("memory", 1073741824.0);
|
||||
bundles.add(bundle);
|
||||
PlacementGroupCreationOptions.Builder builder =
|
||||
new PlacementGroupCreationOptions.Builder()
|
||||
.setBundles(bundles)
|
||||
.setStrategy(PlacementStrategy.PACK);
|
||||
PlacementGroup placementGroup = PlacementGroups.createPlacementGroup(builder.build());
|
||||
Assert.assertTrue(placementGroup.wait(60));
|
||||
|
||||
ActorHandle<Counter> actor =
|
||||
Ray.actor(Counter::new, 1).setPlacementGroup(placementGroup, 0).remote();
|
||||
Assert.assertNotEquals(actor.getId(), ActorId.NIL);
|
||||
Assert.assertEquals(actor.task(Counter::getValue).remote().get(3000), Integer.valueOf(1));
|
||||
|
||||
Assert.assertEquals(
|
||||
Ray.task(PlacementGroupWithResourcesTest::simpleFunction)
|
||||
.setPlacementGroup(placementGroup, 0)
|
||||
.remote()
|
||||
.get(3000),
|
||||
Integer.valueOf(1));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.runtime.object.ObjectRefImpl;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class PlasmaFreeTest extends BaseTest {
|
||||
|
||||
private static String hello() {
|
||||
return "hello";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteObjects() {
|
||||
ObjectRef<String> helloId = Ray.task(PlasmaFreeTest::hello).remote();
|
||||
String helloString = helloId.get();
|
||||
Assert.assertEquals("hello", helloString);
|
||||
Ray.internal().free(ImmutableList.of(helloId), true);
|
||||
|
||||
final boolean result =
|
||||
TestUtils.waitForCondition(
|
||||
() ->
|
||||
!TestUtils.getRuntime()
|
||||
.getObjectStore()
|
||||
.wait(ImmutableList.of(((ObjectRefImpl<String>) helloId).getId()), 1, 0, true)
|
||||
.get(0),
|
||||
50);
|
||||
if (TestUtils.isLocalMode()) {
|
||||
Assert.assertTrue(result);
|
||||
} else {
|
||||
// The object will not be deleted under cluster mode.
|
||||
Assert.assertFalse(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.runtime.config.RayConfig;
|
||||
import io.ray.runtime.config.RunMode;
|
||||
import java.util.List;
|
||||
import org.testng.IAlterSuiteListener;
|
||||
import org.testng.xml.XmlGroups;
|
||||
import org.testng.xml.XmlRun;
|
||||
import org.testng.xml.XmlSuite;
|
||||
|
||||
public class RayAlterSuiteListener implements IAlterSuiteListener {
|
||||
|
||||
@Override
|
||||
public void alter(List<XmlSuite> suites) {
|
||||
XmlSuite suite = suites.get(0);
|
||||
String excludedGroup = RayConfig.create().runMode == RunMode.LOCAL ? "cluster" : "local";
|
||||
XmlGroups groups = new XmlGroups();
|
||||
XmlRun run = new XmlRun();
|
||||
run.onExclude(excludedGroup);
|
||||
groups.setRun(run);
|
||||
suite.setGroups(groups);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.id.ObjectId;
|
||||
import io.ray.runtime.util.SystemConfig;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/** Test Ray.call API */
|
||||
public class RayCallTest extends BaseTest {
|
||||
|
||||
private static byte[] LARGE_RAW_DATA = null;
|
||||
|
||||
private static byte[] getLargeRawData() {
|
||||
if (LARGE_RAW_DATA == null) {
|
||||
LARGE_RAW_DATA = new byte[(int) SystemConfig.getLargestSizePassedByValue() + 100];
|
||||
}
|
||||
return LARGE_RAW_DATA;
|
||||
}
|
||||
|
||||
private static int testInt(int val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
private static byte testByte(byte val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
private static byte[] testBytes(byte[] val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
private static short testShort(short val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
private static long testLong(long val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
private static double testDouble(double val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
private static float testFloat(float val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
private static boolean testBool(boolean val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
private static String testString(String val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
private static List<Integer> testList(List<Integer> val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
private static Map<String, Integer> testMap(Map<String, Integer> val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
private static TestUtils.LargeObject testLargeObject(TestUtils.LargeObject largeObject) {
|
||||
return largeObject;
|
||||
}
|
||||
|
||||
private static void testNoReturn(ObjectId objectId) {
|
||||
// Put an object in object store to inform driver that this function is executing.
|
||||
TestUtils.getRuntime().getObjectStore().put(1, objectId);
|
||||
}
|
||||
|
||||
private static ByteBuffer testByteBuffer(ByteBuffer buffer) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/** Test calling and returning different types. */
|
||||
@Test
|
||||
public void testType() {
|
||||
Assert.assertEquals(1, (int) Ray.task(RayCallTest::testInt, 1).remote().get());
|
||||
Assert.assertEquals(1, (byte) Ray.task(RayCallTest::testByte, (byte) 1).remote().get());
|
||||
Assert.assertEquals(1, (short) Ray.task(RayCallTest::testShort, (short) 1).remote().get());
|
||||
Assert.assertEquals(1, (long) Ray.task(RayCallTest::testLong, 1L).remote().get());
|
||||
Assert.assertEquals(1.0, Ray.task(RayCallTest::testDouble, 1.0).remote().get(), 0.0);
|
||||
Assert.assertEquals(1.0f, Ray.task(RayCallTest::testFloat, 1.0f).remote().get(), 0.0);
|
||||
Assert.assertTrue(Ray.task(RayCallTest::testBool, true).remote().get());
|
||||
Assert.assertEquals("foo", Ray.task(RayCallTest::testString, "foo").remote().get());
|
||||
List<Integer> list = ImmutableList.of(1, 2, 3);
|
||||
Assert.assertEquals(list, Ray.task(RayCallTest::testList, list).remote().get());
|
||||
Map<String, Integer> map = ImmutableMap.of("1", 1, "2", 2);
|
||||
Assert.assertEquals(map, Ray.task(RayCallTest::testMap, map).remote().get());
|
||||
TestUtils.LargeObject largeObject = new TestUtils.LargeObject();
|
||||
Assert.assertNotNull(Ray.task(RayCallTest::testLargeObject, largeObject).remote().get());
|
||||
ByteBuffer buffer1 = ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8));
|
||||
ByteBuffer buffer2 = Ray.task(RayCallTest::testByteBuffer, buffer1).remote().get();
|
||||
byte[] bytes = new byte[buffer2.remaining()];
|
||||
buffer2.get(bytes);
|
||||
Assert.assertEquals("foo", new String(bytes, StandardCharsets.UTF_8));
|
||||
|
||||
// TODO(edoakes): this test doesn't work now that we've switched to direct call
|
||||
// mode. To make it work, we need to implement the same protocol for resolving
|
||||
// passed ObjectIDs that we have in Python.
|
||||
// ObjectId randomObjectId = ObjectId.fromRandom();
|
||||
// Ray.task(RayCallTest::testNoReturn, randomObjectId).remote();
|
||||
// Assert.assertEquals(((int) Ray.get(randomObjectId, Integer.class)), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBytesType() {
|
||||
Assert.assertEquals(
|
||||
"123".getBytes(), Ray.task(RayCallTest::testBytes, "123".getBytes()).remote().get());
|
||||
}
|
||||
|
||||
private static int testNoParam() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int testOneParam(int a) {
|
||||
return a;
|
||||
}
|
||||
|
||||
private static int testTwoParams(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
private static int testThreeParams(int a, int b, int c) {
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
private static int testFourParams(int a, int b, int c, int d) {
|
||||
return a + b + c + d;
|
||||
}
|
||||
|
||||
private static int testFiveParams(int a, int b, int c, int d, int e) {
|
||||
return a + b + c + d + e;
|
||||
}
|
||||
|
||||
private static int testSixParams(int a, int b, int c, int d, int e, int f) {
|
||||
return a + b + c + d + e + f;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNumberOfParameters() {
|
||||
Assert.assertEquals(0, (int) Ray.task(RayCallTest::testNoParam).remote().get());
|
||||
Assert.assertEquals(1, (int) Ray.task(RayCallTest::testOneParam, 1).remote().get());
|
||||
Assert.assertEquals(2, (int) Ray.task(RayCallTest::testTwoParams, 1, 1).remote().get());
|
||||
Assert.assertEquals(3, (int) Ray.task(RayCallTest::testThreeParams, 1, 1, 1).remote().get());
|
||||
Assert.assertEquals(4, (int) Ray.task(RayCallTest::testFourParams, 1, 1, 1, 1).remote().get());
|
||||
Assert.assertEquals(
|
||||
5, (int) Ray.task(RayCallTest::testFiveParams, 1, 1, 1, 1, 1).remote().get());
|
||||
Assert.assertEquals(
|
||||
6, (int) Ray.task(RayCallTest::testSixParams, 1, 1, 1, 1, 1, 1).remote().get());
|
||||
}
|
||||
|
||||
private static Boolean testLargeRawData(byte[] data) {
|
||||
return Arrays.equals(data, getLargeRawData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLargeRawDataArgument() {
|
||||
Assert.assertTrue(Ray.task(RayCallTest::testLargeRawData, getLargeRawData()).remote().get());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.id.JobId;
|
||||
import io.ray.runtime.util.SystemUtil;
|
||||
import java.io.File;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class RayJavaLoggingTest extends BaseTest {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RayJavaLoggingTest.class);
|
||||
|
||||
private static class HeavyLoggingActor {
|
||||
public int getPid() {
|
||||
return SystemUtil.pid();
|
||||
}
|
||||
|
||||
public boolean log() {
|
||||
for (int i = 0; i < 100000; ++i) {
|
||||
LOG.info("hello world, this is a log.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void testJavaLoggingRotate() {
|
||||
ActorHandle<HeavyLoggingActor> loggingActor =
|
||||
Ray.actor(HeavyLoggingActor::new)
|
||||
.setJvmOptions(
|
||||
ImmutableList.of(
|
||||
"-Dray.logging.max-file-size=1KB", "-Dray.logging.max-backup-files=3"))
|
||||
.remote();
|
||||
Assert.assertTrue(loggingActor.task(HeavyLoggingActor::log).remote().get());
|
||||
final int pid = loggingActor.task(HeavyLoggingActor::getPid).remote().get();
|
||||
// Note(qwang): Due to log4j2 is async, once the `log()` actor method returned, we
|
||||
// still have no confident to make sure all log messages are printed to the log files,
|
||||
// especially on slow CI machine.
|
||||
boolean rotated =
|
||||
TestUtils.waitForCondition(
|
||||
() -> {
|
||||
final JobId jobId = Ray.getRuntimeContext().getCurrentJobId();
|
||||
String currLogDir = "/tmp/ray/session_latest/logs";
|
||||
for (int i = 1; i <= 3; ++i) {
|
||||
File rotatedFile =
|
||||
new File(
|
||||
String.format("%s/java-worker-%s-%d.%d.log", currLogDir, jobId, pid, i));
|
||||
if (!rotatedFile.exists()) {
|
||||
return false;
|
||||
}
|
||||
long fileSize = rotatedFile.length();
|
||||
boolean fileSizeExpected = fileSize > 1024 && fileSize < 1024 * 2;
|
||||
if (!fileSizeExpected) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
10 * 1000);
|
||||
Assert.assertTrue(rotated);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.WaitResult;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/** Integration test for Ray.* */
|
||||
public class RayMethodsTest extends BaseTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
ObjectRef<Integer> i1Id = Ray.put(1);
|
||||
ObjectRef<Double> f1Id = Ray.put(3.14);
|
||||
ObjectRef<String> s1Id = Ray.put(String.valueOf("Hello "));
|
||||
ObjectRef<String> s2Id = Ray.put(String.valueOf("World!"));
|
||||
ObjectRef<Object> n1Id = Ray.put(null);
|
||||
|
||||
WaitResult<String> res = Ray.wait(ImmutableList.of(s1Id, s2Id), 2, 1000);
|
||||
|
||||
List<String> ss = res.getReady().stream().map(ObjectRef::get).collect(Collectors.toList());
|
||||
int i1 = i1Id.get();
|
||||
double f1 = f1Id.get();
|
||||
Object n1 = n1Id.get();
|
||||
|
||||
Assert.assertEquals("Hello World!", ss.get(0) + ss.get(1));
|
||||
Assert.assertEquals(1, i1);
|
||||
Assert.assertEquals(3.14, f1, Double.MIN_NORMAL);
|
||||
Assert.assertNull(n1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.PyActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.function.PyActorClass;
|
||||
import io.ray.runtime.object.NativeRayObject;
|
||||
import io.ray.runtime.object.ObjectSerializer;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class RaySerializerTest extends BaseTest {
|
||||
|
||||
@Test
|
||||
public void testSerializePyActor() {
|
||||
PyActorHandle pyActor = Ray.actor(PyActorClass.of("test", "RaySerializerTest")).remote();
|
||||
NativeRayObject nativeRayObject = ObjectSerializer.serialize(pyActor);
|
||||
PyActorHandle result =
|
||||
(PyActorHandle) ObjectSerializer.deserialize(nativeRayObject, null, Object.class);
|
||||
Assert.assertEquals(result.getId(), pyActor.getId());
|
||||
Assert.assertEquals(result.getModuleName(), "test");
|
||||
Assert.assertEquals(result.getClassName(), "RaySerializerTest");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class RedisPasswordTest extends BaseTest {
|
||||
|
||||
@BeforeClass
|
||||
public void setUp() {
|
||||
System.setProperty("ray.redis.username", "default");
|
||||
System.setProperty("ray.redis.password", "12345678");
|
||||
}
|
||||
|
||||
public static String echo(String str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedisPassword() {
|
||||
ObjectRef<String> obj = Ray.task(RedisPasswordTest::echo, "hello").remote();
|
||||
Assert.assertEquals("hello", obj.get());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.Gson;
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.exception.RayException;
|
||||
import io.ray.api.id.ObjectId;
|
||||
import io.ray.runtime.object.NativeObjectStore;
|
||||
import io.ray.runtime.object.ObjectRefImpl;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class ReferenceCountingTest extends BaseTest {
|
||||
@BeforeClass
|
||||
public void setUp() {
|
||||
System.setProperty("ray.head-args.0", "--object-store-memory=" + 100L * 1024 * 1024);
|
||||
}
|
||||
|
||||
/**
|
||||
* Because we can't explicitly GC an Java object. We use this helper method to manually remove an
|
||||
* local reference.
|
||||
*/
|
||||
private static void del(ObjectRef<?> obj) {
|
||||
try {
|
||||
Field referencesField = ObjectRefImpl.class.getDeclaredField("REFERENCES");
|
||||
referencesField.setAccessible(true);
|
||||
Set<?> references = (Set<?>) referencesField.get(null);
|
||||
Class<?> referenceClass =
|
||||
Class.forName("io.ray.runtime.object.ObjectRefImpl$ObjectRefImplReference");
|
||||
Method finalizeReferentMethod = referenceClass.getDeclaredMethod("finalizeReferent");
|
||||
finalizeReferentMethod.setAccessible(true);
|
||||
for (Object reference : references) {
|
||||
if (obj.equals(((Reference<?>) reference).get())) {
|
||||
finalizeReferentMethod.invoke(reference);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkRefCounts(Map<ObjectId, long[]> expected, Duration timeout) {
|
||||
Instant start = Instant.now();
|
||||
while (true) {
|
||||
Map<ObjectId, long[]> actual =
|
||||
((NativeObjectStore) TestUtils.getRuntime().getObjectStore()).getAllReferenceCounts();
|
||||
try {
|
||||
Assert.assertEqualsDeep(actual, expected);
|
||||
return;
|
||||
} catch (AssertionError e) {
|
||||
if (Duration.between(start, Instant.now()).compareTo(timeout) >= 0) {
|
||||
System.out.println("Actual: " + new Gson().toJson(actual));
|
||||
System.out.println("Expected: " + new Gson().toJson(expected));
|
||||
throw e;
|
||||
} else {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkRefCounts(Map<ObjectId, long[]> expected) {
|
||||
checkRefCounts(expected, Duration.ofSeconds(10));
|
||||
}
|
||||
|
||||
private void checkRefCounts(ObjectId objectId, long localRefCount, long submittedTaskRefCount) {
|
||||
checkRefCounts(ImmutableMap.of(objectId, new long[] {localRefCount, submittedTaskRefCount}));
|
||||
}
|
||||
|
||||
private void checkRefCounts(
|
||||
ObjectId objectId1,
|
||||
long localRefCount1,
|
||||
long submittedTaskRefCount1,
|
||||
ObjectId objectId2,
|
||||
long localRefCount2,
|
||||
long submittedTaskRefCount2) {
|
||||
checkRefCounts(
|
||||
ImmutableMap.of(
|
||||
objectId1,
|
||||
new long[] {localRefCount1, submittedTaskRefCount1},
|
||||
objectId2,
|
||||
new long[] {localRefCount2, submittedTaskRefCount2}));
|
||||
}
|
||||
|
||||
private static void fillObjectStoreAndGet(ObjectId objectId, boolean succeed) {
|
||||
fillObjectStoreAndGet(objectId, succeed, 40 * 1024 * 1024, 5);
|
||||
}
|
||||
|
||||
private static void fillObjectStoreAndGet(
|
||||
ObjectId objectId, boolean succeed, int objectSize, int numObjects) {
|
||||
for (int i = 0; i < numObjects; i++) {
|
||||
Ray.put(new TestUtils.LargeObject(objectSize));
|
||||
}
|
||||
if (succeed) {
|
||||
TestUtils.getRuntime().getObjectStore().getRaw(ImmutableList.of(objectId), Long.MAX_VALUE);
|
||||
} else {
|
||||
try {
|
||||
List<Boolean> result =
|
||||
TestUtils.getRuntime().getObjectStore().wait(ImmutableList.of(objectId), 0, 100, true);
|
||||
Assert.fail(
|
||||
"Ray did not fail when waiting for an object that does not belong in this session");
|
||||
} catch (RayException e) {
|
||||
// This is the expected outcome for succeed=false, as we wait for non-existent objects.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Based on Python test case `test_local_refcounts`. */
|
||||
public void testLocalRefCounts() {
|
||||
ObjectRefImpl<Object> obj1 = (ObjectRefImpl<Object>) Ray.put(null);
|
||||
checkRefCounts(obj1.getId(), 1, 0);
|
||||
ObjectRef<Object> obj1Copy = new ObjectRefImpl<>(obj1.getId(), obj1.getType());
|
||||
checkRefCounts(obj1.getId(), 2, 0);
|
||||
|
||||
del(obj1);
|
||||
checkRefCounts(obj1.getId(), 1, 0);
|
||||
del(obj1Copy);
|
||||
checkRefCounts(ImmutableMap.of());
|
||||
}
|
||||
|
||||
private static int oneDep(Object obj) {
|
||||
return oneDep(obj, null);
|
||||
}
|
||||
|
||||
private static int oneDep(Object obj, ActorHandle<SignalActor> singal) {
|
||||
return oneDep(obj, singal, false);
|
||||
}
|
||||
|
||||
private static int oneDep(Object obj, ActorHandle<SignalActor> singal, boolean fail) {
|
||||
if (singal != null) {
|
||||
singal.task(SignalActor::waitSignal).remote().get();
|
||||
}
|
||||
if (fail) {
|
||||
throw new RuntimeException("failed on purpose");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static TestUtils.LargeObject oneDepLarge(Object obj, ActorHandle<SignalActor> singal) {
|
||||
if (singal != null) {
|
||||
singal.task(SignalActor::waitSignal).remote().get();
|
||||
}
|
||||
// This will be spilled to plasma.
|
||||
return new TestUtils.LargeObject(10 * 1024 * 1024);
|
||||
}
|
||||
|
||||
private static void sendSignal(ActorHandle<SignalActor> signal) {
|
||||
ObjectRef<Integer> result = signal.task(SignalActor::sendSignal).remote();
|
||||
result.get();
|
||||
// Remove the reference immediately, otherwise it will affect subsequent tests.
|
||||
del(result);
|
||||
}
|
||||
|
||||
/** Based on Python test case `test_dependency_refcounts`. */
|
||||
public void testDependencyRefCounts1() {
|
||||
{
|
||||
// Test that inlined dependency refcounts are decremented once they are
|
||||
// inlined.
|
||||
ActorHandle<SignalActor> signal = SignalActor.create();
|
||||
ObjectRefImpl<Integer> dep =
|
||||
(ObjectRefImpl<Integer>)
|
||||
Ray.<Integer, ActorHandle<SignalActor>, Integer>task(
|
||||
ReferenceCountingTest::oneDep, Integer.valueOf(1), signal)
|
||||
.remote();
|
||||
checkRefCounts(dep.getId(), 1, 0);
|
||||
ObjectRefImpl<Object> result =
|
||||
(ObjectRefImpl<Object>)
|
||||
Ray.<Integer, Object>task(ReferenceCountingTest::oneDep, dep).remote();
|
||||
checkRefCounts(dep.getId(), 1, 1, result.getId(), 1, 0);
|
||||
sendSignal(signal);
|
||||
// Reference count should be removed as soon as the dependency is inlined.
|
||||
checkRefCounts(dep.getId(), 1, 0, result.getId(), 1, 0);
|
||||
del(dep);
|
||||
del(result);
|
||||
checkRefCounts(ImmutableMap.of());
|
||||
}
|
||||
}
|
||||
|
||||
public void testDependencyRefCounts2() {
|
||||
{
|
||||
// Test that spilled plasma dependency refcounts are decremented once
|
||||
// the task finishes.
|
||||
ActorHandle<SignalActor> signal1 = SignalActor.create();
|
||||
ActorHandle<SignalActor> signal2 = SignalActor.create();
|
||||
ObjectRefImpl<TestUtils.LargeObject> dep =
|
||||
(ObjectRefImpl<TestUtils.LargeObject>)
|
||||
Ray.<TestUtils.LargeObject, ActorHandle<SignalActor>, TestUtils.LargeObject>task(
|
||||
ReferenceCountingTest::oneDepLarge, (TestUtils.LargeObject) null, signal1)
|
||||
.remote();
|
||||
checkRefCounts(dep.getId(), 1, 0);
|
||||
ObjectRefImpl<Integer> result =
|
||||
(ObjectRefImpl<Integer>)
|
||||
Ray.<TestUtils.LargeObject, ActorHandle<SignalActor>, Integer>task(
|
||||
ReferenceCountingTest::oneDep, dep, signal2)
|
||||
.remote();
|
||||
checkRefCounts(dep.getId(), 1, 1, result.getId(), 1, 0);
|
||||
sendSignal(signal1);
|
||||
dep.get(); // TODO(kfstorm): timeout=10
|
||||
// Reference count should remain because the dependency is in plasma.
|
||||
checkRefCounts(dep.getId(), 1, 1, result.getId(), 1, 0);
|
||||
sendSignal(signal2);
|
||||
// Reference count should be removed because the task finished.
|
||||
checkRefCounts(dep.getId(), 1, 0, result.getId(), 1, 0);
|
||||
del(dep);
|
||||
del(result);
|
||||
checkRefCounts(ImmutableMap.of());
|
||||
}
|
||||
}
|
||||
|
||||
public void testDependencyRefCounts3() {
|
||||
{
|
||||
// Test that regular plasma dependency refcounts are decremented if a task
|
||||
// fails.
|
||||
ActorHandle<SignalActor> signal = SignalActor.create();
|
||||
ObjectRefImpl<TestUtils.LargeObject> largeDep =
|
||||
(ObjectRefImpl<TestUtils.LargeObject>)
|
||||
Ray.put(new TestUtils.LargeObject(10 * 1024 * 1024));
|
||||
ObjectRefImpl<Integer> result =
|
||||
(ObjectRefImpl<Integer>)
|
||||
Ray.<TestUtils.LargeObject, ActorHandle<SignalActor>, Boolean, Integer>task(
|
||||
ReferenceCountingTest::oneDep, largeDep, signal, /* fail= */ true)
|
||||
.remote();
|
||||
checkRefCounts(largeDep.getId(), 1, 1, result.getId(), 1, 0);
|
||||
sendSignal(signal);
|
||||
// Reference count should be removed once the task finishes.
|
||||
checkRefCounts(largeDep.getId(), 1, 0, result.getId(), 1, 0);
|
||||
del(largeDep);
|
||||
del(result);
|
||||
checkRefCounts(ImmutableMap.of());
|
||||
}
|
||||
}
|
||||
|
||||
public void testDependencyRefCounts4() {
|
||||
{
|
||||
// Test that spilled plasma dependency refcounts are decremented if a task
|
||||
// fails.
|
||||
ActorHandle<SignalActor> signal1 = SignalActor.create();
|
||||
ActorHandle<SignalActor> signal2 = SignalActor.create();
|
||||
ObjectRefImpl<TestUtils.LargeObject> dep =
|
||||
(ObjectRefImpl<TestUtils.LargeObject>)
|
||||
Ray.<Integer, ActorHandle<SignalActor>, TestUtils.LargeObject>task(
|
||||
ReferenceCountingTest::oneDepLarge, (Integer) null, signal1)
|
||||
.remote();
|
||||
checkRefCounts(dep.getId(), 1, 0);
|
||||
ObjectRefImpl<Integer> result =
|
||||
(ObjectRefImpl<Integer>)
|
||||
Ray.<TestUtils.LargeObject, ActorHandle<SignalActor>, Boolean, Integer>task(
|
||||
ReferenceCountingTest::oneDep, dep, signal2, /* fail= */ true)
|
||||
.remote();
|
||||
checkRefCounts(dep.getId(), 1, 1, result.getId(), 1, 0);
|
||||
sendSignal(signal1);
|
||||
dep.get(); // TODO(kfstorm): timeout=10
|
||||
// Reference count should remain because the dependency is in plasma.
|
||||
checkRefCounts(dep.getId(), 1, 1, result.getId(), 1, 0);
|
||||
sendSignal(signal2);
|
||||
// Reference count should be removed because the task finished.
|
||||
checkRefCounts(dep.getId(), 1, 0, result.getId(), 1, 0);
|
||||
del(dep);
|
||||
del(result);
|
||||
checkRefCounts(ImmutableMap.of());
|
||||
}
|
||||
}
|
||||
|
||||
public void testDependencyRefCounts5() {
|
||||
{
|
||||
// Test that regular plasma dependency refcounts are decremented once the
|
||||
// task finishes.
|
||||
ActorHandle<SignalActor> signal = SignalActor.create();
|
||||
ObjectRefImpl<TestUtils.LargeObject> largeDep =
|
||||
(ObjectRefImpl<TestUtils.LargeObject>) Ray.put(new TestUtils.LargeObject());
|
||||
ObjectRefImpl<Object> result =
|
||||
(ObjectRefImpl<Object>)
|
||||
Ray.<TestUtils.LargeObject, ActorHandle<SignalActor>, Object>task(
|
||||
ReferenceCountingTest::oneDep, largeDep, signal)
|
||||
.remote();
|
||||
checkRefCounts(largeDep.getId(), 1, 1, result.getId(), 1, 0);
|
||||
sendSignal(signal);
|
||||
// Reference count should be removed once the task finishes.
|
||||
checkRefCounts(largeDep.getId(), 1, 0, result.getId(), 1, 0);
|
||||
del(largeDep);
|
||||
del(result);
|
||||
checkRefCounts(ImmutableMap.of());
|
||||
}
|
||||
}
|
||||
|
||||
private static int fooBasicPinning(Object arg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static class ActorBasicPinning {
|
||||
private ObjectRef<TestUtils.LargeObject> largeObject;
|
||||
|
||||
public ActorBasicPinning() {
|
||||
// Hold a long-lived reference to a ray.put object's ID. The object
|
||||
// should not be garbage collected while the actor is alive because
|
||||
// the object is pinned by the raylet.
|
||||
largeObject = Ray.put(new TestUtils.LargeObject(25 * 1024 * 1024));
|
||||
}
|
||||
|
||||
public TestUtils.LargeObject getLargeObject() {
|
||||
return largeObject.get();
|
||||
}
|
||||
}
|
||||
|
||||
/** Based on Python test case `test_basic_pinning`. */
|
||||
public void testBasicPinning() {
|
||||
ActorHandle<ActorBasicPinning> actor = Ray.actor(ActorBasicPinning::new).remote();
|
||||
|
||||
// Fill up the object store with short-lived objects. These should be
|
||||
// evicted before the long-lived object whose reference is held by
|
||||
// the actor.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ObjectRef<Integer> intermediateResult =
|
||||
Ray.task(
|
||||
ReferenceCountingTest::fooBasicPinning,
|
||||
new TestUtils.LargeObject(10 * 1024 * 1024))
|
||||
.remote();
|
||||
intermediateResult.get();
|
||||
}
|
||||
// The ray.get below would fail with only LRU eviction, as the object
|
||||
// that was ray.put by the actor would have been evicted.
|
||||
actor.task(ActorBasicPinning::getLargeObject).remote().get();
|
||||
}
|
||||
|
||||
private static Object pending(TestUtils.LargeObject input1, Integer input2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Based on Python test case `test_pending_task_dependency_pinning`. */
|
||||
public void testPendingTaskDependencyPinning() {
|
||||
// The object that is ray.put here will go out of scope immediately, so if
|
||||
// pending task dependencies aren't considered, it will be evicted before
|
||||
// the ray.get below due to the subsequent ray.puts that fill up the object
|
||||
// store.
|
||||
TestUtils.LargeObject input1 = new TestUtils.LargeObject(40 * 1024 * 1024);
|
||||
ActorHandle<SignalActor> signal = SignalActor.create();
|
||||
ObjectRef<Object> result =
|
||||
Ray.task(
|
||||
ReferenceCountingTest::pending,
|
||||
input1,
|
||||
signal.task(SignalActor::waitSignal).remote())
|
||||
.remote();
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Ray.put(new TestUtils.LargeObject(40 * 1024 * 1024));
|
||||
}
|
||||
|
||||
sendSignal(signal);
|
||||
result.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that an object containing object IDs within it pins the inner IDs. Based on Python test
|
||||
* case `test_basic_nested_ids`.
|
||||
*/
|
||||
public void testBasicNestedIds() {
|
||||
ObjectRefImpl<byte[]> inner = (ObjectRefImpl<byte[]>) Ray.put(new byte[40 * 1024 * 1024]);
|
||||
ObjectRef<List<ObjectRef<byte[]>>> outer = Ray.put(Collections.singletonList(inner));
|
||||
|
||||
// Remove the local reference to the inner object.
|
||||
del(inner);
|
||||
|
||||
// Check that the outer reference pins the inner object.
|
||||
fillObjectStoreAndGet(inner.getId(), true);
|
||||
|
||||
// Remove the outer reference and check that the inner object gets evicted.
|
||||
del(outer);
|
||||
fillObjectStoreAndGet(inner.getId(), false);
|
||||
}
|
||||
|
||||
// TODO(kfstorm): Add more test cases
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.WaitResult;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/** Resources Management Test. */
|
||||
@Test(groups = {"cluster"})
|
||||
public class ResourcesManagementTest extends BaseTest {
|
||||
|
||||
@BeforeClass
|
||||
public void setUp() {
|
||||
System.setProperty("ray.head-args.0", "--num-cpus=4");
|
||||
System.setProperty("ray.head-args.1", "--resources={\"RES-A\":4}");
|
||||
}
|
||||
|
||||
public static Integer echo(Integer number) {
|
||||
return number;
|
||||
}
|
||||
|
||||
public static class Echo {
|
||||
|
||||
public Integer echo(Integer number) {
|
||||
return number;
|
||||
}
|
||||
}
|
||||
|
||||
public void testMethods() {
|
||||
// This is a case that can satisfy required resources.
|
||||
// The static resources for test are "CPU:4,RES-A:4".
|
||||
ObjectRef<Integer> result1 =
|
||||
Ray.task(ResourcesManagementTest::echo, 100).setResource("CPU", 4.0).remote();
|
||||
Assert.assertEquals(100, (int) result1.get());
|
||||
|
||||
// This is a case that can't satisfy required resources.
|
||||
// The static resources for test are "CPU:4,RES-A:4".
|
||||
final ObjectRef<Integer> result2 =
|
||||
Ray.task(ResourcesManagementTest::echo, 200).setResource("CPU", 4.0).remote();
|
||||
WaitResult<Integer> waitResult = Ray.wait(ImmutableList.of(result2), 1, 1000);
|
||||
|
||||
Assert.assertEquals(1, waitResult.getReady().size());
|
||||
Assert.assertEquals(0, waitResult.getUnready().size());
|
||||
}
|
||||
|
||||
public void testActors() {
|
||||
// This is a case that can satisfy required resources.
|
||||
// The static resources for test are "CPU:4,RES-A:4".
|
||||
ActorHandle<Echo> echo1 = Ray.actor(Echo::new).setResource("CPU", 2.0).remote();
|
||||
final ObjectRef<Integer> result1 = echo1.task(Echo::echo, 100).remote();
|
||||
Assert.assertEquals(100, (int) result1.get());
|
||||
|
||||
// This is a case that can't satisfy required resources.
|
||||
// The static resources for test are "CPU:4,RES-A:4".
|
||||
ActorHandle<Echo> echo2 = Ray.actor(Echo::new).setResource("CPU", 8.0).remote();
|
||||
final ObjectRef<Integer> result2 = echo2.task(Echo::echo, 100).remote();
|
||||
WaitResult<Integer> waitResult = Ray.wait(ImmutableList.of(result2), 1, 1000);
|
||||
|
||||
Assert.assertEquals(0, waitResult.getReady().size());
|
||||
Assert.assertEquals(1, waitResult.getUnready().size());
|
||||
}
|
||||
|
||||
public void testSpecifyZeroCPU() {
|
||||
ActorHandle<Echo> echo = Ray.actor(Echo::new).setResource("CPU", 0.0).remote();
|
||||
final ObjectRef<Integer> result1 = echo.task(Echo::echo, 100).remote();
|
||||
Assert.assertEquals(100, (int) result1.get());
|
||||
}
|
||||
|
||||
public void testSpecifyZeroCustomResource() {
|
||||
ActorHandle<Echo> echo = Ray.actor(Echo::new).setResource("A", 0.0).remote();
|
||||
final ObjectRef<Integer> result1 = echo.task(Echo::echo, 100).remote();
|
||||
Assert.assertEquals(100, (int) result1.get());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package io.ray.test;
|
||||
|
||||
import org.testng.IRetryAnalyzer;
|
||||
import org.testng.ITestResult;
|
||||
|
||||
public class RetryAnalyzer implements IRetryAnalyzer {
|
||||
|
||||
private int counter = 0;
|
||||
private static final int RETRY_LIMIT = 2;
|
||||
|
||||
@Override
|
||||
public boolean retry(ITestResult result) {
|
||||
if (counter < RETRY_LIMIT) {
|
||||
counter++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.id.ActorId;
|
||||
import io.ray.api.id.JobId;
|
||||
import io.ray.api.id.TaskId;
|
||||
import io.ray.api.id.UniqueId;
|
||||
import io.ray.api.runtimecontext.NodeInfo;
|
||||
import io.ray.runtime.gcs.GcsClient;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = "cluster")
|
||||
public class RuntimeContextTest extends BaseTest {
|
||||
|
||||
private static JobId JOB_ID = getJobId();
|
||||
|
||||
private static JobId getJobId() {
|
||||
// Must be stable across different processes.
|
||||
byte[] bytes = new byte[JobId.LENGTH];
|
||||
Arrays.fill(bytes, (byte) 127);
|
||||
return JobId.fromByteBuffer(ByteBuffer.wrap(bytes));
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public void setUp() {
|
||||
System.setProperty("ray.job.id", JOB_ID.toString());
|
||||
}
|
||||
|
||||
public void testRuntimeContextInDriver() {
|
||||
Assert.assertEquals(JOB_ID, Ray.getRuntimeContext().getCurrentJobId());
|
||||
Assert.assertNotEquals(Ray.getRuntimeContext().getCurrentTaskId(), TaskId.NIL);
|
||||
}
|
||||
|
||||
public static class RuntimeContextTester {
|
||||
|
||||
public String testRuntimeContext(ActorId actorId) {
|
||||
/// test getCurrentJobId
|
||||
Assert.assertEquals(JOB_ID, Ray.getRuntimeContext().getCurrentJobId());
|
||||
/// test getCurrentTaskId
|
||||
Assert.assertNotEquals(Ray.getRuntimeContext().getCurrentTaskId(), TaskId.NIL);
|
||||
/// test getCurrentActorId
|
||||
Assert.assertEquals(actorId, Ray.getRuntimeContext().getCurrentActorId());
|
||||
|
||||
/// test getCurrentNodeId
|
||||
UniqueId currNodeId = Ray.getRuntimeContext().getCurrentNodeId();
|
||||
GcsClient gcsClient = TestUtils.getRuntime().getGcsClient();
|
||||
List<NodeInfo> allNodeInfo = gcsClient.getAllNodeInfo();
|
||||
Assert.assertEquals(allNodeInfo.size(), 1);
|
||||
Assert.assertEquals(allNodeInfo.get(0).nodeId, currNodeId);
|
||||
|
||||
return "ok";
|
||||
}
|
||||
}
|
||||
|
||||
public void testRuntimeContextInActor() {
|
||||
ActorHandle<RuntimeContextTester> actor = Ray.actor(RuntimeContextTester::new).remote();
|
||||
Assert.assertEquals(
|
||||
"ok", actor.task(RuntimeContextTester::testRuntimeContext, actor.getId()).remote().get());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
package io.ray.test;
|
||||
|
||||
import static io.ray.api.runtimeenv.types.RuntimeEnvName.JARS;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.runtimeenv.RuntimeEnv;
|
||||
import io.ray.api.runtimeenv.RuntimeEnvConfig;
|
||||
import io.ray.api.runtimeenv.types.RuntimeEnvName;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = "cluster")
|
||||
public class RuntimeEnvTest {
|
||||
|
||||
private static final String FOO_JAR_URL =
|
||||
"https://github.com/ray-project/test_packages/raw/main/raw_resources/foo.jar";
|
||||
private static final String BAR_JAR_URL =
|
||||
"https://github.com/ray-project/test_packages/raw/main/raw_resources/bar.jar";
|
||||
private static final String FOO_ZIP_URL =
|
||||
"https://github.com/ray-project/test_packages/raw/main/raw_resources/foo.zip";
|
||||
private static final String BAR_ZIP_URL =
|
||||
"https://github.com/ray-project/test_packages/raw/main/raw_resources/bar.zip";
|
||||
|
||||
private static final String FOO_CLASS_NAME = "io.testpackages.Foo";
|
||||
private static final String BAR_CLASS_NAME = "io.testpackages.Bar";
|
||||
|
||||
private static class A {
|
||||
|
||||
public String getEnv(String key) {
|
||||
return System.getenv(key);
|
||||
}
|
||||
|
||||
public boolean findClass(String className) {
|
||||
try {
|
||||
Class.forName(className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void testPerJobEnvVars() {
|
||||
System.setProperty("ray.job.runtime-env.env-vars.KEY1", "A");
|
||||
System.setProperty("ray.job.runtime-env.env-vars.KEY2", "B");
|
||||
|
||||
try {
|
||||
Ray.init();
|
||||
ActorHandle<A> actor = Ray.actor(A::new).remote();
|
||||
String val = actor.task(A::getEnv, "KEY1").remote().get();
|
||||
Assert.assertEquals(val, "A");
|
||||
val = actor.task(A::getEnv, "KEY2").remote().get();
|
||||
Assert.assertEquals(val, "B");
|
||||
} finally {
|
||||
System.clearProperty("ray.job.runtime-env.env-vars.KEY1");
|
||||
System.clearProperty("ray.job.runtime-env.env-vars.KEY2");
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private static String getEnvVar(String key) {
|
||||
return System.getenv(key);
|
||||
}
|
||||
|
||||
public void testEnvVarsForNormalTask() {
|
||||
try {
|
||||
Ray.init();
|
||||
RuntimeEnv runtimeEnv = new RuntimeEnv.Builder().build();
|
||||
Map<String, String> envMap =
|
||||
new HashMap<String, String>(ImmutableMap.of("KEY1", "A", "KEY2", "B", "KEY3", "C"));
|
||||
runtimeEnv.set(RuntimeEnvName.ENV_VARS, envMap);
|
||||
|
||||
String val =
|
||||
Ray.task(RuntimeEnvTest::getEnvVar, "KEY1").setRuntimeEnv(runtimeEnv).remote().get();
|
||||
Assert.assertEquals(val, "A");
|
||||
val = Ray.task(RuntimeEnvTest::getEnvVar, "KEY2").setRuntimeEnv(runtimeEnv).remote().get();
|
||||
Assert.assertEquals(val, "B");
|
||||
} finally {
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/// overwrite the runtime env from job config.
|
||||
public void testPerTaskEnvVarsOverwritePerJobEnvVars() {
|
||||
System.setProperty("ray.job.runtime-env.env-vars.KEY1", "A");
|
||||
System.setProperty("ray.job.runtime-env.env-vars.KEY2", "B");
|
||||
try {
|
||||
Ray.init();
|
||||
Map<String, String> envMap = new HashMap<String, String>(ImmutableMap.of("KEY1", "C"));
|
||||
RuntimeEnv runtimeEnv = new RuntimeEnv.Builder().build();
|
||||
runtimeEnv.set(RuntimeEnvName.ENV_VARS, envMap);
|
||||
|
||||
/// value of KEY1 is overwritten to `C` and KEY2s is extended from job config.
|
||||
String val =
|
||||
Ray.task(RuntimeEnvTest::getEnvVar, "KEY1").setRuntimeEnv(runtimeEnv).remote().get();
|
||||
Assert.assertEquals(val, "C");
|
||||
val = Ray.task(RuntimeEnvTest::getEnvVar, "KEY2").setRuntimeEnv(runtimeEnv).remote().get();
|
||||
Assert.assertEquals(val, "B");
|
||||
} finally {
|
||||
System.clearProperty("ray.job.runtime-env.env-vars.KEY1");
|
||||
System.clearProperty("ray.job.runtime-env.env-vars.KEY2");
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private static void testDownloadAndLoadPackage(String url) {
|
||||
try {
|
||||
Ray.init();
|
||||
RuntimeEnv runtimeEnv = new RuntimeEnv.Builder().build();
|
||||
runtimeEnv.set(JARS, ImmutableList.of(url));
|
||||
ActorHandle<A> actor1 = Ray.actor(A::new).setRuntimeEnv(runtimeEnv).remote();
|
||||
boolean ret = actor1.task(A::findClass, FOO_CLASS_NAME).remote().get();
|
||||
Assert.assertTrue(ret);
|
||||
} finally {
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public void testJarPackageInActor() {
|
||||
testDownloadAndLoadPackage(FOO_JAR_URL);
|
||||
}
|
||||
|
||||
public void testZipPackageInActor() {
|
||||
testDownloadAndLoadPackage(FOO_ZIP_URL);
|
||||
}
|
||||
|
||||
private static boolean findClasses(List<String> classNames) {
|
||||
try {
|
||||
for (String name : classNames) {
|
||||
Class.forName(name);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void testDownloadAndLoadPackagesForTask(
|
||||
List<String> urls, List<String> classNames) {
|
||||
try {
|
||||
Ray.init();
|
||||
RuntimeEnv runtimeEnv = new RuntimeEnv.Builder().build();
|
||||
runtimeEnv.set(JARS, urls);
|
||||
boolean ret =
|
||||
Ray.task(RuntimeEnvTest::findClasses, classNames)
|
||||
.setRuntimeEnv(runtimeEnv)
|
||||
.remote()
|
||||
.get();
|
||||
Assert.assertTrue(ret);
|
||||
} finally {
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private static void testDownloadAndLoadPackagesForTask(String url, String className) {
|
||||
testDownloadAndLoadPackagesForTask(ImmutableList.of(url), ImmutableList.of(className));
|
||||
}
|
||||
|
||||
public void testJarPackageForTask() {
|
||||
testDownloadAndLoadPackagesForTask(BAR_JAR_URL, BAR_CLASS_NAME);
|
||||
}
|
||||
|
||||
public void testZipPackageForTask() {
|
||||
testDownloadAndLoadPackagesForTask(FOO_ZIP_URL, FOO_CLASS_NAME);
|
||||
}
|
||||
|
||||
/// This case tests that a task needs 2 jars for load different classes.
|
||||
public void testMultipleJars() {
|
||||
testDownloadAndLoadPackagesForTask(
|
||||
ImmutableList.of(FOO_JAR_URL, BAR_JAR_URL),
|
||||
ImmutableList.of(BAR_CLASS_NAME, FOO_CLASS_NAME));
|
||||
}
|
||||
|
||||
public void testRuntimeEnvJarsForJob() {
|
||||
System.setProperty("ray.job.runtime-env.jars.0", FOO_JAR_URL);
|
||||
System.setProperty("ray.job.runtime-env.jars.1", BAR_JAR_URL);
|
||||
try {
|
||||
Ray.init();
|
||||
boolean ret =
|
||||
Ray.task(RuntimeEnvTest::findClasses, ImmutableList.of(BAR_CLASS_NAME, FOO_CLASS_NAME))
|
||||
.remote()
|
||||
.get();
|
||||
Assert.assertTrue(ret);
|
||||
} finally {
|
||||
System.clearProperty("ray.job.runtime-env.jars.0");
|
||||
System.clearProperty("ray.job.runtime-env.jars.1");
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private static class Pip {
|
||||
private String[] packages;
|
||||
private Boolean pipCheck;
|
||||
|
||||
public String[] getPackages() {
|
||||
return packages;
|
||||
}
|
||||
|
||||
public void setPackages(String[] packages) {
|
||||
this.packages = packages;
|
||||
}
|
||||
|
||||
public Boolean getPipCheck() {
|
||||
return pipCheck;
|
||||
}
|
||||
|
||||
public void setPipCheck(Boolean pipCheck) {
|
||||
this.pipCheck = pipCheck;
|
||||
}
|
||||
}
|
||||
|
||||
public void testRuntimeEnvAPI() {
|
||||
try {
|
||||
Ray.init();
|
||||
RuntimeEnv runtimeEnv = new RuntimeEnv.Builder().build();
|
||||
String workingDir = "https://path/to/working_dir.zip";
|
||||
runtimeEnv.set("working_dir", workingDir);
|
||||
String[] pyModules =
|
||||
new String[] {"https://path/to/py_modules1.zip", "https://path/to/py_modules2.zip"};
|
||||
runtimeEnv.set("py_modules", pyModules);
|
||||
Pip pip = new Pip();
|
||||
pip.setPackages(new String[] {"requests", "tensorflow"});
|
||||
pip.setPipCheck(true);
|
||||
runtimeEnv.set("pip", pip);
|
||||
String serializedRuntimeEnv = runtimeEnv.serialize();
|
||||
|
||||
RuntimeEnv runtimeEnv2 = RuntimeEnv.deserialize(serializedRuntimeEnv);
|
||||
Assert.assertEquals(runtimeEnv2.get("working_dir", String.class), workingDir);
|
||||
Assert.assertEquals(runtimeEnv2.get("py_modules", String[].class), pyModules);
|
||||
Pip pip2 = runtimeEnv2.get("pip", Pip.class);
|
||||
Assert.assertEquals(pip2.getPackages(), pip.getPackages());
|
||||
Assert.assertEquals(pip2.getPipCheck(), pip.getPipCheck());
|
||||
|
||||
Assert.assertEquals(runtimeEnv2.remove("working_dir"), true);
|
||||
Assert.assertEquals(runtimeEnv2.remove("py_modules"), true);
|
||||
Assert.assertEquals(runtimeEnv2.remove("pip"), true);
|
||||
Assert.assertEquals(runtimeEnv2.remove("conda"), false);
|
||||
Assert.assertEquals(runtimeEnv2.get("working_dir", String.class), null);
|
||||
Assert.assertEquals(runtimeEnv2.get("py_modules", String[].class), null);
|
||||
Assert.assertEquals(runtimeEnv2.get("pip", Pip.class), null);
|
||||
} finally {
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public void testRuntimeEnvJsonStringAPI() {
|
||||
try {
|
||||
Ray.init();
|
||||
RuntimeEnv runtimeEnv = new RuntimeEnv.Builder().build();
|
||||
String pipString = "{\"packages\":[\"requests\",\"tensorflow\"],\"pip_check\":false}";
|
||||
runtimeEnv.setJsonStr("pip", pipString);
|
||||
String serializedRuntimeEnv = runtimeEnv.serialize();
|
||||
|
||||
RuntimeEnv runtimeEnv2 = RuntimeEnv.deserialize(serializedRuntimeEnv);
|
||||
Assert.assertEquals(runtimeEnv2.getJsonStr("pip"), pipString);
|
||||
} finally {
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public void testRuntimeEnvContextForJob() {
|
||||
System.setProperty("ray.job.runtime-env.jars.0", FOO_JAR_URL);
|
||||
System.setProperty("ray.job.runtime-env.jars.1", BAR_JAR_URL);
|
||||
System.setProperty("ray.job.runtime-env.config.setup-timeout-seconds", "1");
|
||||
try {
|
||||
Ray.init();
|
||||
RuntimeEnv runtimeEnv = Ray.getRuntimeContext().getCurrentRuntimeEnv();
|
||||
Assert.assertNotNull(runtimeEnv);
|
||||
List<String> jars = runtimeEnv.get(JARS, List.class);
|
||||
Assert.assertNotNull(jars);
|
||||
Assert.assertEquals(jars.size(), 2);
|
||||
Assert.assertEquals(jars.get(0), FOO_JAR_URL);
|
||||
Assert.assertEquals(jars.get(1), BAR_JAR_URL);
|
||||
RuntimeEnvConfig runtimeEnvConfig = runtimeEnv.getConfig();
|
||||
Assert.assertNotNull(runtimeEnvConfig);
|
||||
Assert.assertEquals((int) runtimeEnvConfig.getSetupTimeoutSeconds(), 1);
|
||||
|
||||
} finally {
|
||||
System.clearProperty("ray.job.runtime-env.jars.0");
|
||||
System.clearProperty("ray.job.runtime-env.jars.1");
|
||||
System.clearProperty("ray.job.runtime-env.config.setup-timeout-seconds");
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private static Integer getRuntimeEnvTimeout() {
|
||||
RuntimeEnv runtimeEnv = Ray.getRuntimeContext().getCurrentRuntimeEnv();
|
||||
if (runtimeEnv != null) {
|
||||
return runtimeEnv.getConfig().getSetupTimeoutSeconds();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void testRuntimeEnvContextForTask() {
|
||||
try {
|
||||
Ray.init();
|
||||
RuntimeEnv currentRuntimeEnv = Ray.getRuntimeContext().getCurrentRuntimeEnv();
|
||||
Assert.assertTrue(currentRuntimeEnv.isEmpty());
|
||||
RuntimeEnv runtimeEnv = new RuntimeEnv.Builder().build();
|
||||
RuntimeEnvConfig runtimeEnvConfig = new RuntimeEnvConfig(1, false);
|
||||
runtimeEnv.setConfig(runtimeEnvConfig);
|
||||
Integer result =
|
||||
Ray.task(RuntimeEnvTest::getRuntimeEnvTimeout).setRuntimeEnv(runtimeEnv).remote().get();
|
||||
Assert.assertNotNull(result);
|
||||
Assert.assertEquals((int) result, 1);
|
||||
} finally {
|
||||
Ray.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test
|
||||
public class SetupJavaWorkerTest extends BaseTest {
|
||||
|
||||
private static final String CODE_SEARCH_PATH = System.getProperty("java.class.path");;
|
||||
|
||||
public static class Counter {
|
||||
|
||||
private int value;
|
||||
|
||||
public Counter(int initValue) {
|
||||
this.value = initValue;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public void setup() {
|
||||
// mock a fake code search path contains whitespace
|
||||
String newCodeSearchPathStr = CODE_SEARCH_PATH + ":fakepathStart fakePathEnd";
|
||||
System.setProperty("java.class.path", newCodeSearchPathStr);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public void clean() {
|
||||
// restore class path
|
||||
System.setProperty("java.class.path", CODE_SEARCH_PATH);
|
||||
}
|
||||
/**
|
||||
* raylet will call setup_worker.py with arguments to start java workers after Ray 2.0.0, and
|
||||
* setup_worker.py can not start java workers correctly when argumens contian whitespace. This
|
||||
* test case is used to test whether the bug is fixed.
|
||||
*/
|
||||
public void testCreateActorFail() {
|
||||
ActorHandle<ActorTest.Counter> actor = Ray.actor(ActorTest.Counter::new, 1).remote();
|
||||
// throw RayTimeoutException exception if actor is not started correctly by raylet
|
||||
Integer value = actor.task(ActorTest.Counter::getValue).remote().get(30 * 1000);
|
||||
Assert.assertEquals(Integer.valueOf(1), value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
public class SignalActor {
|
||||
|
||||
private Semaphore semaphore;
|
||||
|
||||
public SignalActor() {
|
||||
this.semaphore = new Semaphore(0);
|
||||
}
|
||||
|
||||
public int sendSignal() {
|
||||
this.semaphore.release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int waitSignal() throws InterruptedException {
|
||||
this.semaphore.acquire();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static ActorHandle<SignalActor> create() {
|
||||
return Ray.actor(SignalActor::new).setMaxConcurrency(2).remote();
|
||||
}
|
||||
|
||||
public int ping() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class StressTest extends BaseTest {
|
||||
|
||||
public static int echo(int x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
public void testSubmittingTasks() {
|
||||
for (int numIterations : ImmutableList.of(1, 10, 100, 1000)) {
|
||||
int numTasks = 1000 / numIterations;
|
||||
for (int i = 0; i < numIterations; i++) {
|
||||
List<ObjectRef<Integer>> results = new ArrayList<>();
|
||||
for (int j = 0; j < numTasks; j++) {
|
||||
results.add(Ray.task(StressTest::echo, 1).remote());
|
||||
}
|
||||
|
||||
for (Integer result : Ray.get(results)) {
|
||||
Assert.assertEquals(result, Integer.valueOf(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testDependency() {
|
||||
ObjectRef<Integer> x = Ray.task(StressTest::echo, 1).remote();
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
x = Ray.task(StressTest::echo, x).remote();
|
||||
}
|
||||
|
||||
Assert.assertEquals(x.get(), Integer.valueOf(1));
|
||||
}
|
||||
|
||||
public static class Actor {
|
||||
|
||||
public int ping() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Worker {
|
||||
|
||||
private ActorHandle<Actor> actor;
|
||||
|
||||
public Worker(ActorHandle<Actor> actor) {
|
||||
this.actor = actor;
|
||||
}
|
||||
|
||||
public int ping(int n) {
|
||||
List<ObjectRef<Integer>> objectRefs = new ArrayList<>();
|
||||
for (int i = 0; i < n; i++) {
|
||||
objectRefs.add(actor.task(Actor::ping).remote());
|
||||
}
|
||||
int sum = 0;
|
||||
for (Integer result : Ray.get(objectRefs)) {
|
||||
sum += result;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
|
||||
public void testSubmittingManyTasksToOneActor() throws Exception {
|
||||
ActorHandle<Actor> actor = Ray.actor(Actor::new).remote();
|
||||
List<ObjectRef<Integer>> objectRefs = new ArrayList<>();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ActorHandle<Worker> worker = Ray.actor(Worker::new, actor).remote();
|
||||
objectRefs.add(worker.task(Worker::ping, 100).remote());
|
||||
}
|
||||
|
||||
for (Integer result : Ray.get(objectRefs)) {
|
||||
Assert.assertEquals(result, Integer.valueOf(100));
|
||||
}
|
||||
}
|
||||
|
||||
public void testPuttingAndGettingManyObjects() {
|
||||
Integer objectToPut = 1;
|
||||
List<ObjectRef<Integer>> objects = new ArrayList<>();
|
||||
for (int i = 0; i < 100_000; i++) {
|
||||
objects.add(Ray.put(objectToPut));
|
||||
}
|
||||
|
||||
for (ObjectRef<Integer> object : objects) {
|
||||
Assert.assertEquals(object.get(), objectToPut);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.runtime.util.SystemConfig;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = "cluster")
|
||||
public class SystemConfigTest extends BaseTest {
|
||||
|
||||
public void testDefaultConfigs() {
|
||||
long ret = ((Double) SystemConfig.get("max_direct_call_object_size")).longValue();
|
||||
Assert.assertEquals(ret, 100 * 1024);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.List;
|
||||
import org.testng.IClassListener;
|
||||
import org.testng.ITestClass;
|
||||
|
||||
public class SystemPropertyListener implements IClassListener {
|
||||
private final List<String> whitelist = ImmutableList.of("ray.run-mode");
|
||||
|
||||
@Override
|
||||
public void onAfterClass(ITestClass testClass) {
|
||||
for (String key : System.getProperties().stringPropertyNames()) {
|
||||
if (key.startsWith("ray.")) {
|
||||
if (!whitelist.stream().anyMatch(key::equals)) {
|
||||
System.clearProperty(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.exception.RayTaskException;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class TaskExceptionTest extends BaseTest {
|
||||
|
||||
private static class UnserializableClass {}
|
||||
|
||||
private static class UnserializableException extends RuntimeException {
|
||||
|
||||
public UnserializableException() {
|
||||
super();
|
||||
}
|
||||
|
||||
private UnserializableClass unSerializableClass = new UnserializableClass();
|
||||
}
|
||||
|
||||
private static class MyActor {
|
||||
|
||||
public String sayHi() {
|
||||
return "Hi";
|
||||
}
|
||||
|
||||
public String throwUnserializableException() {
|
||||
throw new UnserializableException();
|
||||
}
|
||||
}
|
||||
|
||||
private static String throwUnserializableException() {
|
||||
throw new UnserializableException();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThrowUnserializableExceptionInNormalTask() {
|
||||
// Test that if a task throws an unserializable exception, the worker won't crash.
|
||||
Assert.assertThrows(
|
||||
(() -> Ray.task(TaskExceptionTest::throwUnserializableException).remote().get()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThrowUnserializableExceptionInActorTask() {
|
||||
ActorHandle<MyActor> myActor = Ray.actor(MyActor::new).remote();
|
||||
Assert.assertEquals("Hi", myActor.task(MyActor::sayHi).remote().get());
|
||||
Assert.assertThrows((() -> myActor.task(MyActor::throwUnserializableException).remote().get()));
|
||||
}
|
||||
|
||||
private static String hello() {
|
||||
Ray.task(TaskExceptionTest::throwUnserializableException).remote().get();
|
||||
return "hello";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThrowRootExceptionForChainedTasks() {
|
||||
RayTaskException ex =
|
||||
Assert.expectThrows(
|
||||
RayTaskException.class, () -> Ray.task(TaskExceptionTest::hello).remote().get());
|
||||
Assert.assertTrue(ex.getCause() instanceof RayTaskException);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.Ray;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/** Task Name Test. */
|
||||
public class TaskNameTest extends BaseTest {
|
||||
|
||||
private static int testFoo() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Test setting task name at task submission time. */
|
||||
@Test
|
||||
public void testSetName() {
|
||||
Assert.assertEquals(0, (int) Ray.task(TaskNameTest::testFoo).setName("foo").remote().get());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.ray.test;
|
||||
|
||||
public class TestActor {
|
||||
|
||||
public TestActor(byte[] v) {
|
||||
value = v;
|
||||
}
|
||||
|
||||
public byte[] concat(byte[] v) {
|
||||
byte[] c = new byte[value.length + v.length];
|
||||
System.arraycopy(value, 0, c, 0, value.length);
|
||||
System.arraycopy(v, 0, c, value.length, v.length);
|
||||
return c;
|
||||
}
|
||||
|
||||
public byte[] getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
private byte[] value;
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.ray.runtime.runner.RunManager;
|
||||
import java.io.File;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.testng.IInvokedMethod;
|
||||
import org.testng.IInvokedMethodListener;
|
||||
import org.testng.ITestContext;
|
||||
import org.testng.ITestListener;
|
||||
import org.testng.ITestResult;
|
||||
import org.testng.SkipException;
|
||||
|
||||
public class TestProgressListener implements IInvokedMethodListener, ITestListener {
|
||||
|
||||
// Travis aborts CI if no outputs for 10 minutes. So threshold needs to be smaller than 10m.
|
||||
private static final long hangDetectionThresholdMillis = 5 * 60 * 1000;
|
||||
private static final int TAIL_NO_OF_LINES = 500;
|
||||
private Thread testMainThread;
|
||||
private long testStartTimeMillis;
|
||||
|
||||
private String getFullTestName(ITestResult testResult) {
|
||||
return testResult.getTestClass().getName() + "." + testResult.getMethod().getMethodName();
|
||||
}
|
||||
|
||||
private void printSection(String sectionName) {
|
||||
System.out.println(
|
||||
"============ [" + LocalDateTime.now().toString() + "] " + sectionName + " ============");
|
||||
}
|
||||
|
||||
private void printTestStage(String tag, String content) {
|
||||
printSection("[" + tag + "] " + content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {}
|
||||
|
||||
@Override
|
||||
public void afterInvocation(IInvokedMethod method, ITestResult testResult) {}
|
||||
|
||||
@Override
|
||||
public void onTestStart(ITestResult result) {
|
||||
printTestStage("TEST START", getFullTestName(result));
|
||||
testStartTimeMillis = System.currentTimeMillis();
|
||||
// TODO(kfstorm): Add a timer to detect hang
|
||||
if (testMainThread == null) {
|
||||
testMainThread = Thread.currentThread();
|
||||
Thread hangDetectionThread =
|
||||
new Thread(
|
||||
() -> {
|
||||
try {
|
||||
// If current task case has ran for more than 5 minutes.
|
||||
while (System.currentTimeMillis() - testStartTimeMillis
|
||||
< hangDetectionThresholdMillis) {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
printDebugInfo(null, /*testHanged=*/ true);
|
||||
} catch (InterruptedException e) {
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
hangDetectionThread.setDaemon(true);
|
||||
hangDetectionThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestSuccess(ITestResult result) {
|
||||
printTestStage("TEST SUCCESS", getFullTestName(result));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestFailure(ITestResult result) {
|
||||
printTestStage("TEST FAILURE", getFullTestName(result));
|
||||
printDebugInfo(result, /*testHanged=*/ false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestSkipped(ITestResult result) {
|
||||
printTestStage("TEST SKIPPED", getFullTestName(result));
|
||||
printDebugInfo(result, /*testHanged=*/ false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
|
||||
printTestStage("TEST FAILED BUT WITHIN SUCCESS PERCENTAGE", getFullTestName(result));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(ITestContext context) {}
|
||||
|
||||
@Override
|
||||
public void onFinish(ITestContext context) {}
|
||||
|
||||
private void printDebugInfo(ITestResult result, boolean testHanged) {
|
||||
boolean testFailed = false;
|
||||
if (result != null) {
|
||||
Throwable throwable = result.getThrowable();
|
||||
if (throwable != null && !(throwable instanceof SkipException)) {
|
||||
testFailed = true;
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (!testFailed && !testHanged) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (testHanged) {
|
||||
if (result != null) {
|
||||
printSection("TEST CASE HANGED: " + getFullTestName(result));
|
||||
} else {
|
||||
printSection("TEST CASE HANGED");
|
||||
}
|
||||
printSection("TEST CASE HANGED");
|
||||
printSection("STACK TRACE OF TEST THREAD");
|
||||
for (StackTraceElement element : testMainThread.getStackTrace()) {
|
||||
System.out.println(element.toString());
|
||||
}
|
||||
Set<Integer> javaPids = getJavaPids();
|
||||
for (Integer pid : javaPids) {
|
||||
runCommandSafely(ImmutableList.of("jstack", pid.toString()));
|
||||
// TODO(kfstorm): Check lldb or gdb exists rather than detecting OS type.
|
||||
if (SystemUtils.IS_OS_MAC) {
|
||||
runCommandSafely(
|
||||
ImmutableList.of("lldb", "--batch", "-o", "bt all", "-p", pid.toString()));
|
||||
} else {
|
||||
runCommandSafely(
|
||||
ImmutableList.of(
|
||||
"sudo", "gdb", "-batch", "-ex", "thread apply all bt", "-p", pid.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printLogFiles();
|
||||
|
||||
if (testHanged) {
|
||||
printSection("ABORT TEST");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private String runCommandSafely(List<String> command) {
|
||||
String output;
|
||||
String commandString = String.join(" ", command);
|
||||
printSection(commandString);
|
||||
try {
|
||||
output = RunManager.runCommand(command);
|
||||
System.out.println(output);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to execute command: " + commandString);
|
||||
e.printStackTrace();
|
||||
output = "";
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
private Set<Integer> getJavaPids() {
|
||||
Set<Integer> javaPids = new HashSet<>();
|
||||
String jpsOutput = runCommandSafely(ImmutableList.of("jps", "-v"));
|
||||
try {
|
||||
for (String line : StringUtils.split(jpsOutput, "\n")) {
|
||||
String[] parts = StringUtils.split(line);
|
||||
if (parts.length > 1 && parts[1].toLowerCase().equals("jps")) {
|
||||
// Skip jps.
|
||||
continue;
|
||||
}
|
||||
Integer pid = Integer.valueOf(parts[0]);
|
||||
javaPids.add(pid);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to parse jps output.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
String pgrepJavaResult = runCommandSafely(ImmutableList.of("pgrep", "java"));
|
||||
try {
|
||||
for (String line : StringUtils.split(pgrepJavaResult, "\n")) {
|
||||
Integer pid = Integer.valueOf(line);
|
||||
javaPids.add(pid);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to parse pgrep java output.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return javaPids;
|
||||
}
|
||||
|
||||
private void printLogFiles() {
|
||||
Collection<File> logFiles =
|
||||
FileUtils.listFiles(new File("/tmp/ray/session_latest/logs"), null, false);
|
||||
for (File file : logFiles) {
|
||||
runCommandSafely(
|
||||
ImmutableList.of("tail", "-n", String.valueOf(TAIL_NO_OF_LINES), file.getAbsolutePath()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.runtime.AbstractRayRuntime;
|
||||
import io.ray.runtime.config.RayConfig;
|
||||
import io.ray.runtime.config.RunMode;
|
||||
import io.ray.runtime.util.SystemConfig;
|
||||
import java.io.Serializable;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import org.testng.Assert;
|
||||
|
||||
public class TestUtils {
|
||||
|
||||
public static class LargeObject implements Serializable {
|
||||
|
||||
public byte[] data;
|
||||
|
||||
public LargeObject() {
|
||||
this(1024 * 1024);
|
||||
}
|
||||
|
||||
public LargeObject(int size) {
|
||||
Preconditions.checkState(size > SystemConfig.getLargestSizePassedByValue());
|
||||
data = new byte[size];
|
||||
}
|
||||
}
|
||||
|
||||
private static final int WAIT_INTERVAL_MS = 5;
|
||||
|
||||
public static boolean isLocalMode() {
|
||||
return getRuntime().getRayConfig().runMode == RunMode.LOCAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the given runnable finishes within given timeout.
|
||||
*
|
||||
* @param runnable A runnable that should finish within given timeout.
|
||||
* @param timeoutMs Timeout in milliseconds.
|
||||
*/
|
||||
public static void executeWithinTime(Runnable runnable, int timeoutMs) {
|
||||
executeWithinTimeRange(runnable, 0, timeoutMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the given runnable finishes within given time range.
|
||||
*
|
||||
* @param runnable A runnable that should finish within given timeout.
|
||||
* @param minTimeMs The minimum time for execution.
|
||||
* @param maxTimeMs The maximum time for execution.
|
||||
*/
|
||||
public static void executeWithinTimeRange(Runnable runnable, int minTimeMs, int maxTimeMs) {
|
||||
Instant start = Instant.now();
|
||||
runnable.run();
|
||||
Instant end = Instant.now();
|
||||
long duration = Duration.between(start, end).toMillis();
|
||||
Assert.assertTrue(
|
||||
duration >= minTimeMs,
|
||||
"The given runnable didn't run for at least "
|
||||
+ minTimeMs
|
||||
+ "ms. "
|
||||
+ "Actual execution time: "
|
||||
+ duration
|
||||
+ " ms.");
|
||||
Assert.assertTrue(
|
||||
duration <= maxTimeMs,
|
||||
"The given runnable didn't finish within "
|
||||
+ maxTimeMs
|
||||
+ "ms. "
|
||||
+ "Actual execution time: "
|
||||
+ duration
|
||||
+ " ms.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until the given condition is met.
|
||||
*
|
||||
* @param condition A function that predicts the condition.
|
||||
* @param timeoutMs Timeout in milliseconds.
|
||||
* @return True if the condition is met within the timeout, false otherwise.
|
||||
*/
|
||||
public static boolean waitForCondition(Supplier<Boolean> condition, int timeoutMs) {
|
||||
long endTime = System.currentTimeMillis() + timeoutMs;
|
||||
while (true) {
|
||||
if (condition.get()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (System.currentTimeMillis() >= endTime) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
java.util.concurrent.TimeUnit.MILLISECONDS.sleep(WAIT_INTERVAL_MS);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String hi() {
|
||||
return "hi";
|
||||
}
|
||||
|
||||
/**
|
||||
* Warm up the cluster to make sure there's at least one idle worker.
|
||||
*
|
||||
* <p>This is needed before calling `wait`. Because, in Travis CI, starting a new worker process
|
||||
* could be slower than the wait timeout.
|
||||
*
|
||||
* <p>TODO(hchen): We should consider supporting always reversing a certain number of idle workers
|
||||
* in Raylet's worker pool.
|
||||
*/
|
||||
public static void warmUpCluster() {
|
||||
ObjectRef<String> obj = Ray.task(TestUtils::hi).remote();
|
||||
Assert.assertEquals(obj.get(), "hi");
|
||||
}
|
||||
|
||||
public static AbstractRayRuntime getRuntime() {
|
||||
return (AbstractRayRuntime) Ray.internal();
|
||||
}
|
||||
|
||||
public static ProcessBuilder buildDriver(Class<?> mainClass, String[] args) {
|
||||
RayConfig rayConfig = TestUtils.getRuntime().getRayConfig();
|
||||
|
||||
List<String> fullArgs = new ArrayList<>();
|
||||
fullArgs.add("java");
|
||||
fullArgs.add("-cp");
|
||||
fullArgs.add(System.getProperty("java.class.path"));
|
||||
fullArgs.add("-Dray.address=" + rayConfig.getBootstrapAddress());
|
||||
fullArgs.add("-Dray.object-store.socket-name=" + rayConfig.objectStoreSocketName);
|
||||
fullArgs.add("-Dray.raylet.socket-name=" + rayConfig.rayletSocketName);
|
||||
fullArgs.add("-Dray.raylet.node-manager-port=" + rayConfig.getNodeManagerPort());
|
||||
fullArgs.add(mainClass.getName());
|
||||
if (args != null) {
|
||||
fullArgs.addAll(Arrays.asList(args));
|
||||
}
|
||||
|
||||
return new ProcessBuilder(fullArgs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package io.ray.test;
|
||||
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.runtime.util.SystemUtil;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public class UserLoggerTest extends BaseTest {
|
||||
private static final Logger LOG1 = LoggerFactory.getLogger("test_user_logger1");
|
||||
|
||||
private static final Logger LOG2 = LoggerFactory.getLogger("test_user_logger2");
|
||||
|
||||
static final String LOG_CONTEXT = "This is an user log";
|
||||
|
||||
static final String CURR_LOG_DIR = "/tmp/ray/session_latest/logs";
|
||||
|
||||
@BeforeClass
|
||||
public void setupJobConfig() {
|
||||
System.setProperty("ray.job.jvm-options.0", "-Dray.logging.loggers.0.name=test_user_logger1");
|
||||
System.setProperty(
|
||||
"ray.job.jvm-options.1", "-Dray.logging.loggers.0.file-name=test_user_logger-1-%p-%j");
|
||||
System.setProperty(
|
||||
"ray.job.jvm-options.2",
|
||||
"-Dray.logging.loggers.0.pattern=%d{yyyy-MM-dd-HH:mm:ss,SSS}%p,%c{1},[%t]:%m%n");
|
||||
System.setProperty("ray.job.jvm-options.3", "-Dray.logging.loggers.1.name=test_user_logger2");
|
||||
System.setProperty(
|
||||
"ray.job.jvm-options.4", "-Dray.logging.loggers.1.file-name=test_user_logger-2-%p-%j");
|
||||
}
|
||||
|
||||
private static class ActorWithUserLogger {
|
||||
public int getPid() {
|
||||
LOG1.info(LOG_CONTEXT + "1");
|
||||
LOG2.info(LOG_CONTEXT + "2");
|
||||
return SystemUtil.pid();
|
||||
}
|
||||
}
|
||||
|
||||
public void testUserLogger() throws IOException {
|
||||
ActorHandle<ActorWithUserLogger> actor = Ray.actor(ActorWithUserLogger::new).remote();
|
||||
int actorPid = actor.task(ActorWithUserLogger::getPid).remote().get();
|
||||
testUserLogger(actorPid, "1");
|
||||
testUserLogger(actorPid, "2");
|
||||
}
|
||||
|
||||
private void testUserLogger(int pid, String indexStr) throws IOException {
|
||||
File userLoggerFile =
|
||||
new File(
|
||||
CURR_LOG_DIR
|
||||
+ "/test_user_logger-%i-%p-%j.log"
|
||||
.replace("%i", indexStr)
|
||||
.replace("%p", String.valueOf(pid))
|
||||
.replace("%j", Ray.getRuntimeContext().getCurrentJobId().toString()));
|
||||
Assert.assertTrue(userLoggerFile.exists());
|
||||
BufferedReader reader = new BufferedReader(new FileReader(userLoggerFile));
|
||||
String context = reader.readLine();
|
||||
Assert.assertTrue(context.endsWith(LOG_CONTEXT + indexStr));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import io.ray.api.WaitResult;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class WaitTest extends BaseTest {
|
||||
|
||||
private static String hi() {
|
||||
return "hi";
|
||||
}
|
||||
|
||||
private static String delayedHi() {
|
||||
try {
|
||||
Thread.sleep(100 * 1000);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "hi";
|
||||
}
|
||||
|
||||
private static void testWait() {
|
||||
// Call a task in advance to warm up the cluster to avoid being too slow to start workers.
|
||||
TestUtils.warmUpCluster();
|
||||
|
||||
ObjectRef<String> obj1 = Ray.task(WaitTest::hi).remote();
|
||||
ObjectRef<String> obj2 = Ray.task(WaitTest::delayedHi).remote();
|
||||
|
||||
List<ObjectRef<String>> waitList = ImmutableList.of(obj1, obj2);
|
||||
WaitResult<String> waitResult = Ray.wait(waitList, 2, 2 * 1000);
|
||||
|
||||
List<ObjectRef<String>> readyList = waitResult.getReady();
|
||||
|
||||
Assert.assertEquals(1, waitResult.getReady().size());
|
||||
Assert.assertEquals(1, waitResult.getUnready().size());
|
||||
Assert.assertEquals("hi", readyList.get(0).get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWaitInDriver() {
|
||||
testWait();
|
||||
}
|
||||
|
||||
public static Object waitInWorker() {
|
||||
testWait();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWaitInWorker() {
|
||||
ObjectRef<Object> res = Ray.task(WaitTest::waitInWorker).remote();
|
||||
res.get();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWaitForEmpty() {
|
||||
WaitResult<String> result = Ray.wait(new ArrayList<>());
|
||||
Assert.assertTrue(result.getReady().isEmpty());
|
||||
Assert.assertTrue(result.getUnready().isEmpty());
|
||||
|
||||
try {
|
||||
Ray.wait(null);
|
||||
Assert.fail();
|
||||
} catch (NullPointerException e) {
|
||||
Assert.assertTrue(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package io.ray.test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.ray.api.ActorHandle;
|
||||
import io.ray.api.ObjectRef;
|
||||
import io.ray.api.Ray;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class WorkerJvmOptionsTest extends BaseTest {
|
||||
|
||||
public static class Echo {
|
||||
String getOptions() {
|
||||
return System.getProperty("test.suffix");
|
||||
}
|
||||
}
|
||||
|
||||
@Test(groups = {"cluster"})
|
||||
public void testJvmOptions() {
|
||||
// The whitespaces in following argument are intentionally added to test
|
||||
// that raylet can correctly handle dynamic options with whitespaces.
|
||||
ActorHandle<Echo> actor =
|
||||
Ray.actor(Echo::new)
|
||||
.setJvmOptions(
|
||||
ImmutableList.of("-Dtest.suffix=suffix", "-Dtest.suffix1=suffix1 suffix1"))
|
||||
.remote();
|
||||
ObjectRef<String> obj = actor.task(Echo::getOptions).remote();
|
||||
Assert.assertEquals(obj.get(30 * 1000), "suffix");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
# This file is used by CrossLanguageInvocationTest.java to test cross-language
|
||||
# invocation.
|
||||
|
||||
import asyncio
|
||||
|
||||
import ray
|
||||
|
||||
|
||||
@ray.remote
|
||||
def py_return_input(v):
|
||||
return v
|
||||
|
||||
|
||||
@ray.remote
|
||||
def py_func_call_java_function():
|
||||
try:
|
||||
# None
|
||||
r = ray.cross_language.java_function(
|
||||
"io.ray.test.CrossLanguageInvocationTest", "returnInput"
|
||||
).remote(None)
|
||||
assert ray.get(r) is None
|
||||
# bool
|
||||
r = ray.cross_language.java_function(
|
||||
"io.ray.test.CrossLanguageInvocationTest", "returnInputBoolean"
|
||||
).remote(True)
|
||||
assert ray.get(r) is True
|
||||
# int
|
||||
r = ray.cross_language.java_function(
|
||||
"io.ray.test.CrossLanguageInvocationTest", "returnInputInt"
|
||||
).remote(100)
|
||||
assert ray.get(r) == 100
|
||||
# double
|
||||
r = ray.cross_language.java_function(
|
||||
"io.ray.test.CrossLanguageInvocationTest", "returnInputDouble"
|
||||
).remote(1.23)
|
||||
assert ray.get(r) == 1.23
|
||||
# string
|
||||
r = ray.cross_language.java_function(
|
||||
"io.ray.test.CrossLanguageInvocationTest", "returnInputString"
|
||||
).remote("Hello World!")
|
||||
assert ray.get(r) == "Hello World!"
|
||||
# list (tuple will be packed by pickle,
|
||||
# so only list can be transferred across language)
|
||||
r = ray.cross_language.java_function(
|
||||
"io.ray.test.CrossLanguageInvocationTest", "returnInputIntArray"
|
||||
).remote([1, 2, 3])
|
||||
assert ray.get(r) == [1, 2, 3]
|
||||
# pack
|
||||
f = ray.cross_language.java_function(
|
||||
"io.ray.test.CrossLanguageInvocationTest", "pack"
|
||||
)
|
||||
input = [100, "hello", 1.23, [1, "2", 3.0]]
|
||||
r = f.remote(*input)
|
||||
assert ray.get(r) == input
|
||||
return "success"
|
||||
except Exception as ex:
|
||||
return str(ex)
|
||||
|
||||
|
||||
@ray.remote
|
||||
def py_func_call_java_actor(value):
|
||||
assert isinstance(value, bytes)
|
||||
c = ray.cross_language.java_actor_class("io.ray.test.TestActor")
|
||||
java_actor = c.remote(b"Counter")
|
||||
r = java_actor.concat.remote(value)
|
||||
return ray.get(r)
|
||||
|
||||
|
||||
@ray.remote
|
||||
def py_func_call_java_actor_from_handle(actor_handle):
|
||||
r = actor_handle.concat.remote(b"2")
|
||||
return ray.get(r)
|
||||
|
||||
|
||||
@ray.remote
|
||||
def py_func_call_python_actor_from_handle(actor_handle):
|
||||
r = actor_handle.increase.remote(2)
|
||||
return ray.get(r)
|
||||
|
||||
|
||||
@ray.remote
|
||||
def py_func_pass_python_actor_handle():
|
||||
counter = Counter.remote(2)
|
||||
f = ray.cross_language.java_function(
|
||||
"io.ray.test.CrossLanguageInvocationTest", "callPythonActorHandle"
|
||||
)
|
||||
r = f.remote(counter)
|
||||
return ray.get(r)
|
||||
|
||||
|
||||
@ray.remote
|
||||
def py_func_python_raise_exception():
|
||||
_ = 1 / 0
|
||||
|
||||
|
||||
@ray.remote
|
||||
def py_func_java_throw_exception():
|
||||
f = ray.cross_language.java_function(
|
||||
"io.ray.test.CrossLanguageInvocationTest", "throwException"
|
||||
)
|
||||
r = f.remote()
|
||||
return ray.get(r)
|
||||
|
||||
|
||||
@ray.remote
|
||||
def py_func_nest_python_raise_exception():
|
||||
f = ray.cross_language.java_function(
|
||||
"io.ray.test.CrossLanguageInvocationTest", "raisePythonException"
|
||||
)
|
||||
r = f.remote()
|
||||
return ray.get(r)
|
||||
|
||||
|
||||
@ray.remote
|
||||
def py_func_nest_java_throw_exception():
|
||||
f = ray.cross_language.java_function(
|
||||
"io.ray.test.CrossLanguageInvocationTest", "throwJavaException"
|
||||
)
|
||||
r = f.remote()
|
||||
return ray.get(r)
|
||||
|
||||
|
||||
@ray.remote
|
||||
class Counter(object):
|
||||
def __init__(self, value):
|
||||
self.value = int(value)
|
||||
|
||||
def increase(self, delta):
|
||||
self.value += int(delta)
|
||||
return str(self.value).encode("utf-8")
|
||||
|
||||
|
||||
@ray.remote
|
||||
class AsyncCounter(object):
|
||||
async def __init__(self, value):
|
||||
self.value = int(value)
|
||||
self.event = asyncio.Event()
|
||||
|
||||
async def block_task(self):
|
||||
self.event.wait()
|
||||
|
||||
async def increase(self, delta):
|
||||
self.value += int(delta)
|
||||
self.event.set()
|
||||
return str(self.value).encode("utf-8")
|
||||
|
||||
|
||||
@ray.remote
|
||||
class SyncCounter(object):
|
||||
def __init__(self, value):
|
||||
self.value = int(value)
|
||||
self.event = asyncio.Event()
|
||||
|
||||
def block_task(self):
|
||||
self.event.wait()
|
||||
|
||||
def increase(self, delta):
|
||||
self.value += int(delta)
|
||||
return str(self.value).encode("utf-8")
|
||||
|
||||
|
||||
@ray.remote
|
||||
def py_func_create_named_actor():
|
||||
counter = Counter.options(name="py_named_actor", lifetime="detached").remote(100)
|
||||
assert ray.get(counter.increase.remote(1)) == b"101"
|
||||
return b"true"
|
||||
|
||||
|
||||
@ray.remote
|
||||
def py_func_get_and_invoke_named_actor():
|
||||
java_named_actor = ray.get_actor("java_named_actor")
|
||||
assert ray.get(java_named_actor.concat.remote(b"world")) == b"helloworld"
|
||||
return b"true"
|
||||
|
||||
|
||||
@ray.remote
|
||||
def py_func_call_java_overrided_method_with_default_keyword():
|
||||
cls = ray.cross_language.java_actor_class("io.ray.test.ExampleImpl")
|
||||
handle = cls.remote()
|
||||
return ray.get(handle.echo.remote("hi"))
|
||||
|
||||
|
||||
@ray.remote
|
||||
def py_func_call_java_overloaded_method():
|
||||
cls = ray.cross_language.java_actor_class("io.ray.test.ExampleImpl")
|
||||
handle = cls.remote()
|
||||
ref1 = handle.overloadedFunc.remote("first")
|
||||
ref2 = handle.overloadedFunc.remote("first", "second")
|
||||
result = ray.get([ref1, ref2])
|
||||
assert result == ["first", "firstsecond"]
|
||||
return True
|
||||
Reference in New Issue
Block a user