-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #167 from exacaster/local_backend
Add Local backend
- Loading branch information
Showing
22 changed files
with
473 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
.gradle | ||
.env | ||
build | ||
bin | ||
out | ||
generated | ||
lib |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# Lighter on local environment | ||
Besides K8s and YARN cluster modes, it is also possible to run Lighter in local mode. | ||
Local mode is meant for local testing and demo purposes. You can start Lighter locally | ||
by executing `docker-compose up` command inside `./dev/` folder. | ||
|
||
When lighter is running you can execute example applications provided by Apache Spark. | ||
For example `Spark PI` application can be started by executing this `curl` command: | ||
|
||
```bash | ||
curl -X 'POST' \ | ||
'http://localhost:8080/lighter/api/batches' \ | ||
-H 'accept: application/json' \ | ||
-H 'Content-Type: application/json' \ | ||
-d '{ | ||
"name": "Spark PI", | ||
"file": "/home/app/spark/examples/jars/spark-examples_2.12-3.3.0.jar", | ||
"mainClass": "org.apache.spark.examples.SparkPi", | ||
"args": ["100"] | ||
}' | ||
``` | ||
|
||
Lighter UI can be accessed on: [http://localhost:8080/lighter](http://localhost:8080/lighter).\ | ||
You can also explore Lighter API by visiting Swagger UI on [http://localhost:8080/swagger-ui/](http://localhost:8080/swagger-ui/). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
version: "3.9" | ||
services: | ||
lighter: | ||
build: ../ | ||
ports: | ||
- "8080:8080" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
server/src/main/java/com/exacaster/lighter/backend/ClusterSparkListener.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package com.exacaster.lighter.backend; | ||
|
||
import static org.slf4j.LoggerFactory.getLogger; | ||
|
||
import java.util.concurrent.CountDownLatch; | ||
import java.util.function.Consumer; | ||
import org.apache.spark.launcher.SparkAppHandle; | ||
import org.apache.spark.launcher.SparkAppHandle.State; | ||
import org.slf4j.Logger; | ||
|
||
public class ClusterSparkListener implements SparkListener { | ||
|
||
private static final Logger LOG = getLogger(ClusterSparkListener.class); | ||
private final Consumer<Throwable> errorHandler; | ||
private final CountDownLatch latch; | ||
|
||
public ClusterSparkListener(Consumer<Throwable> errorHandler) { | ||
this.errorHandler = errorHandler; | ||
this.latch = new CountDownLatch(1); | ||
} | ||
|
||
@Override | ||
public void stateChanged(SparkAppHandle handle) { | ||
var state = handle.getState(); | ||
LOG.info("State change. AppId: {}, State: {}", handle.getAppId(), state); | ||
handle.getError().ifPresent((error) -> { | ||
LOG.warn("State changed with error: {} ", error.getMessage()); | ||
if (State.FAILED.equals(state)) { | ||
onError(error); | ||
} | ||
}); | ||
// Disconnect when final or submitted. | ||
// In case app fails after detach, status will be retrieved by ApplicationStatusHandler. | ||
if (state != null && (state.isFinal() || State.SUBMITTED.equals(state))) { | ||
handle.disconnect(); | ||
latch.countDown(); | ||
} | ||
} | ||
|
||
@Override | ||
public void waitCompletion() throws InterruptedException { | ||
latch.await(); | ||
} | ||
|
||
@Override | ||
public void onError(Throwable error) { | ||
errorHandler.accept(error); | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
server/src/main/java/com/exacaster/lighter/backend/Constants.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.exacaster.lighter.backend; | ||
|
||
public final class Constants { | ||
public final static String DEPLOY_MODE_CLUSTER = "cluster"; | ||
public final static String DEPLOY_MODE_CLIENT = "client"; | ||
public final static String MASTER_YARN = "yarn"; | ||
|
||
private Constants() { | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 4 additions & 41 deletions
45
server/src/main/java/com/exacaster/lighter/backend/SparkListener.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,51 +1,14 @@ | ||
package com.exacaster.lighter.backend; | ||
|
||
import static org.slf4j.LoggerFactory.getLogger; | ||
|
||
import com.exacaster.lighter.concurrency.Waitable; | ||
import java.util.concurrent.CountDownLatch; | ||
import java.util.function.Consumer; | ||
import org.apache.spark.launcher.SparkAppHandle; | ||
import org.apache.spark.launcher.SparkAppHandle.Listener; | ||
import org.apache.spark.launcher.SparkAppHandle.State; | ||
import org.slf4j.Logger; | ||
|
||
public class SparkListener implements Listener, Waitable { | ||
|
||
private static final Logger LOG = getLogger(SparkListener.class); | ||
private final Consumer<Throwable> errorHandler; | ||
private final CountDownLatch latch; | ||
|
||
public SparkListener(Consumer<Throwable> errorHandler) { | ||
this.errorHandler = errorHandler; | ||
this.latch = new CountDownLatch(1); | ||
} | ||
public interface SparkListener extends Listener, Waitable { | ||
|
||
@Override | ||
public void stateChanged(SparkAppHandle handle) { | ||
var state = handle.getState(); | ||
LOG.info("State change. AppId: {}, State: {}", handle.getAppId(), state); | ||
handle.getError().ifPresent((error) -> { | ||
LOG.warn("State changed with error: {} ", error.getMessage()); | ||
if (State.FAILED.equals(state)) { | ||
errorHandler.accept(error); | ||
} | ||
}); | ||
// Disconnect when final or submitted. | ||
// In case app fails after detach, status will be retrieved by ApplicationStatusHandler. | ||
if (state != null && (state.isFinal() || State.SUBMITTED.equals(state))) { | ||
handle.disconnect(); | ||
latch.countDown(); | ||
} | ||
} | ||
|
||
@Override | ||
public void infoChanged(SparkAppHandle handle) { | ||
// TODO: ? | ||
} | ||
void onError(Throwable error); | ||
|
||
@Override | ||
public void waitCompletion() throws InterruptedException { | ||
latch.await(); | ||
default void infoChanged(SparkAppHandle handle) { | ||
// default: do nothing | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
94 changes: 94 additions & 0 deletions
94
server/src/main/java/com/exacaster/lighter/backend/local/LocalApp.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package com.exacaster.lighter.backend.local; | ||
|
||
import static org.slf4j.LoggerFactory.getLogger; | ||
|
||
import com.exacaster.lighter.application.Application; | ||
import com.exacaster.lighter.application.ApplicationState; | ||
import com.exacaster.lighter.backend.SparkListener; | ||
import com.exacaster.lighter.backend.local.logger.LogCollectingHandler; | ||
import java.util.Objects; | ||
import java.util.Optional; | ||
import java.util.function.Consumer; | ||
import java.util.logging.Logger; | ||
import org.apache.spark.launcher.SparkAppHandle; | ||
import org.apache.spark.launcher.SparkAppHandle.State; | ||
|
||
public class LocalApp implements SparkListener { | ||
private static final org.slf4j.Logger LOG = getLogger(LocalApp.class); | ||
|
||
private final LogCollectingHandler logHandle; | ||
private final String loggerName; | ||
private final Consumer<Throwable> errorHandler; | ||
private SparkAppHandle handle; | ||
|
||
public LocalApp(Application application, Consumer<Throwable> errorHandler) { | ||
this.errorHandler = errorHandler; | ||
this.logHandle = new LogCollectingHandler(500); | ||
this.loggerName = "spark_app_" + application.getId(); | ||
var logger = Logger.getLogger(loggerName); | ||
logger.addHandler(logHandle); | ||
} | ||
|
||
public Optional<ApplicationState> getState() { | ||
if (handle == null) { | ||
return Optional.empty(); | ||
} | ||
|
||
switch (handle.getState()) { | ||
case UNKNOWN: | ||
return Optional.of(ApplicationState.NOT_STARTED); | ||
case CONNECTED: | ||
case SUBMITTED: | ||
case RUNNING: | ||
return Optional.of(ApplicationState.BUSY); | ||
case FINISHED: | ||
return Optional.of(ApplicationState.SUCCESS); | ||
case FAILED: | ||
return Optional.of(ApplicationState.ERROR); | ||
case LOST: | ||
return Optional.of(ApplicationState.DEAD); | ||
case KILLED: | ||
return Optional.of(ApplicationState.KILLED); | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
public String getLog() { | ||
return logHandle.getLogs(); | ||
} | ||
|
||
public void kill() { | ||
Objects.requireNonNull(handle); | ||
handle.kill(); | ||
Logger.getLogger(loggerName).removeHandler(logHandle); | ||
} | ||
|
||
@Override | ||
public void onError(Throwable error) { | ||
errorHandler.accept(error); | ||
} | ||
|
||
@Override | ||
public void waitCompletion() throws InterruptedException { | ||
// Local application is not detached, until it is completed | ||
// do not block application processing for local applications. | ||
} | ||
|
||
@Override | ||
public void stateChanged(SparkAppHandle handle) { | ||
this.handle = handle; | ||
var state = handle.getState(); | ||
LOG.info("State change. AppId: {}, State: {}", handle.getAppId(), state); | ||
handle.getError().ifPresent((error) -> { | ||
LOG.warn("State changed with error: {} ", error.getMessage()); | ||
if (State.FAILED.equals(state)) { | ||
onError(error); | ||
} | ||
}); | ||
} | ||
|
||
public String getLoggerName() { | ||
return loggerName; | ||
} | ||
|
||
} |
Oops, something went wrong.