Skip to content
This repository has been archived by the owner on Oct 14, 2020. It is now read-only.

Commit

Permalink
Gradle Optimization Improvements
Browse files Browse the repository at this point in the history
- when-prewarmed utility added to ensure we wait until the starting daemon is ready
- cleaned up test cases
- improved test case prewarm integration
- increased jvm memory
  • Loading branch information
jhoffner committed Sep 1, 2017
1 parent de8c166 commit 8ba49a6
Show file tree
Hide file tree
Showing 15 changed files with 190 additions and 234 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ frameworks/java/.gradle
frameworks/java/4.0
frameworks/java/build
frameworks/java/buildOutputCleanup
frameworks/gradle/.gradle
frameworks/gradle/build
11 changes: 10 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,16 @@ services:
- ./examples:/runner/examples
- ./frameworks:/runner/frameworks
- ./test:/runner/test
- ./listen.js:/runner/listen.js
entrypoint: ''
command: bash

gradle-runner:
image: codewars/gradle-runner
volumes:
- ./lib:/runner/lib
- ./examples:/runner/examples
- ./frameworks:/runner/frameworks
- ./test:/runner/test
entrypoint: ''
command: bash

Expand Down
5 changes: 3 additions & 2 deletions docker/gradle.docker
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ WORKDIR /runner
COPY package.json package.json
RUN npm install --production

COPY frameworks/gradle frameworks/gradle
COPY frameworks/gradle/build.gradle frameworks/gradle/build.gradle
COPY frameworks/gradle/src frameworks/gradle/src
RUN chown -R codewarrior:codewarrior frameworks/gradle

COPY entrypoint.sh entrypoint.sh
Expand All @@ -39,7 +40,7 @@ USER codewarrior
ENV USER=codewarrior HOME=/home/codewarrior

# JVM flags to control memory usage. org.gradle.jvmargs applies to forked JVM process
ENV GRADLE_OPTS="-Xmx64m -Dorg.gradle.jvmargs='-Xmx256m'"
ENV GRADLE_OPTS="-Xmx64m -Dorg.gradle.jvmargs='-Xmx512m'"

RUN cd /runner/frameworks/gradle \
# download dependencies
Expand Down
2 changes: 1 addition & 1 deletion frameworks/gradle/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ test {
task run(type: JavaExec, dependsOn: classes) {
// use gradle -D?
main = System.getenv("MAIN_CLASS_NAME")
jvmArgs '-Xmx256m'
jvmArgs '-Xmx512m'
classpath sourceSets.main.runtimeClasspath
classpath configurations.runtime
}
1 change: 0 additions & 1 deletion frameworks/gradle/gradle.properties

This file was deleted.

7 changes: 7 additions & 0 deletions frameworks/gradle/prewarm.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
#!/bin/bash

echo "loading" > /workspace/prewarm.status

# prewarm by starting the gradle daemon. Running an initial test build will also speed things up a bit
cd /runner/frameworks/gradle && gradle --daemon --offline test

echo "loaded" > /workspace/prewarm.status

echo "prewarmed"
2 changes: 0 additions & 2 deletions frameworks/java/prewarm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,3 @@ cd /runner/frameworks/java && gradle --daemon --offline test

echo "loaded" > /workspace/prewarm.status

# node run -l java -c "public class Solution {}" -f "import org.junit.Test;public class TestFixture{}"

36 changes: 20 additions & 16 deletions lib/runners/groovy.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const path = require('path');

const fs = require('fs-extra');
const whenPrewarmed = require('../utils/when-prewarmed');

module.exports = {
solutionOnly(opts, runCode) {
Expand Down Expand Up @@ -40,22 +41,25 @@ module.exports = {
fs.outputFileSync(path.join(dir, 'src', 'main', 'groovy', 'Setup.groovy'), opts.setup);
fs.outputFileSync(path.join(dir, 'src', 'main', 'groovy', 'Solution.groovy'), opts.solution);
fs.outputFileSync(path.join(dir, 'src', 'test', 'groovy', 'Fixture.groovy'), opts.fixture);
runCode({
name: 'gradle',
args: [
'--daemon',
'--no-search-upward',
'--offline',
'--exclude-task', 'compileScala',
'--exclude-task', 'compileKotlin',
'--exclude-task', 'compileTestScala',
'--exclude-task', 'compileTestKotlin',
'--quiet',
'test',
],
options: {
cwd: dir,
}

whenPrewarmed(opts, prewarmed => {
runCode({
name: 'gradle',
args: [
prewarmed ? '--daemon' : '--no-daemon', // if not prewarmed don't bother
'--no-search-upward',
'--offline',
'--exclude-task', 'compileScala',
'--exclude-task', 'compileKotlin',
'--exclude-task', 'compileTestScala',
'--exclude-task', 'compileTestKotlin',
'--quiet',
'test',
],
options: {
cwd: dir,
}
});
});
},

Expand Down
28 changes: 3 additions & 25 deletions lib/runners/java.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const fs = require('fs-extra');

const tagHelpers = require('../utils/tag-helpers');
const manipulateFileSync = require('../utils/manipulate-file-sync');
const whenPrewarmed = require('../utils/when-prewarmed');

// we will cache the contents of this file so that we can process it later.
var buildGradle = '';
Expand Down Expand Up @@ -102,29 +103,6 @@ module.exports = {
}
};

/**
* If the prewarming process is in-process, we want to wait until it finishes, otherwise two builds happen and that
* will kill performance and the process will likely fail due to a timeout.
* multiple
* @param cb Function callback. True will be passed to the callback if the prewarm happened, which
* will indicate that the deamon should have been started.
*/
function whenReady(cb, notified, opts) {
const path = "/workspace/prewarm.status";
if (!fs.pathExistsSync(path)) {
cb(false);
}
else if (fs.readFileSync(path).toString().indexOf('loaded') === 0) {
cb(true);
}
else {
if (!notified) {
opts.publish('status', 'Waiting for Gradle daemon to start...');
}
setTimeout(() => whenReady(cb, true, opts), 200);
}
}

// we always build and test code with the gradle test task, even if we are not actually testing anything.
// Currently this is because we have test mode setup to embed its output within the build output.
function buildAndTest(opts, runCode) {
Expand All @@ -140,7 +118,7 @@ function buildAndTest(opts, runCode) {

buildGradle = manipulateFileSync(`/runner/frameworks/java/build.gradle`, `${opts.dir}/build.gradle`, templateOptions);

whenReady(prewarmed => {
whenPrewarmed(opts, prewarmed => {
runCode({
name: 'gradle',
// reuse the java dir cache since thats where we originally built from
Expand All @@ -159,7 +137,7 @@ function buildAndTest(opts, runCode) {
cwd: opts.dir
}
});
}, false, opts);
});
}

// checks if the spring-boot reference is loaded and adds additional settings
Expand Down
35 changes: 19 additions & 16 deletions lib/runners/kotlin.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const path = require('path');

const fs = require('fs-extra');
const whenPrewarmed = require('../utils/when-prewarmed');

module.exports = {
solutionOnly(opts, runCode) {
Expand Down Expand Up @@ -41,22 +42,24 @@ module.exports = {
fs.outputFileSync(path.join(dir, 'src', 'main', 'kotlin', 'setup.kt'), opts.setup);
fs.outputFileSync(path.join(dir, 'src', 'main', 'kotlin', 'solution.kt'), opts.solution);
fs.outputFileSync(path.join(dir, 'src', 'test', 'kotlin', 'fixture.kt'), opts.fixture);
runCode({
name: 'gradle',
args: [
'--daemon',
'--no-search-upward',
'--offline',
'--exclude-task', 'compileScala',
'--exclude-task', 'compileGroovy',
'--exclude-task', 'compileTestScala',
'--exclude-task', 'compileTestGroovy',
'--quiet',
'test',
],
options: {
cwd: dir,
}
whenPrewarmed(opts, prewarmed => {
runCode({
name: 'gradle',
args: [
prewarmed ? '--daemon' : '--no-daemon', // if not prewarmed don't bother
'--no-search-upward',
'--offline',
'--exclude-task', 'compileScala',
'--exclude-task', 'compileGroovy',
'--exclude-task', 'compileTestScala',
'--exclude-task', 'compileTestGroovy',
'--quiet',
'test',
],
options: {
cwd: dir,
}
});
});
},

Expand Down
36 changes: 20 additions & 16 deletions lib/runners/scala.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const path = require('path');

const fs = require('fs-extra');
const whenPrewarmed = require('../utils/when-prewarmed');

module.exports = {
solutionOnly(opts, runCode) {
Expand Down Expand Up @@ -40,22 +41,25 @@ module.exports = {
fs.outputFileSync(path.join(dir, 'src', 'main', 'scala', 'Setup.scala'), opts.setup);
fs.outputFileSync(path.join(dir, 'src', 'main', 'scala', 'Solution.scala'), opts.solution);
fs.outputFileSync(path.join(dir, 'src', 'test', 'scala', 'Fixture.scala'), opts.fixture);
runCode({
name: 'gradle',
args: [
'--daemon',
'--no-search-upward',
'--offline',
'--exclude-task', 'compileGroovy',
'--exclude-task', 'compileKotlin',
'--exclude-task', 'compileTestGroovy',
'--exclude-task', 'compileTestKotlin',
'--quiet',
'test',
],
options: {
cwd: dir,
}

whenPrewarmed(opts, prewarmed => {
runCode({
name: 'gradle',
args: [
prewarmed ? '--daemon' : '--no-daemon', // if not prewarmed don't bother
'--no-search-upward',
'--offline',
'--exclude-task', 'compileGroovy',
'--exclude-task', 'compileKotlin',
'--exclude-task', 'compileTestGroovy',
'--exclude-task', 'compileTestKotlin',
'--quiet',
'test',
],
options: {
cwd: dir,
}
});
});
},

Expand Down
53 changes: 53 additions & 0 deletions lib/utils/when-prewarmed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const fs = require('fs-extra');
const execSync = require('child_process').execSync;

const statusPath = "/home/codewarrior/prewarm.status";

/**
* If the prewarming process is in-process, we want to wait until it finishes, otherwise in the case of things like waiting
* for Gradle, two builds happen and that will kill performance and the process will likely fail due to a timeout.
* @param cb Function callback. True will be passed to the callback if the prewarm happened, which
* will indicate that the deamon should have been started.
*/
function whenPrewarmed(opts, cb, notified) {
if (!fs.pathExistsSync(statusPath)) {
cb(false);
}
else if (fs.readFileSync(statusPath).toString().indexOf('loaded') === 0) {
cb(true);
}
else {
if (!notified && opts.publish) {
opts.publish('status', 'Waiting for prewarming to finish...');
}
setTimeout(() => whenPrewarmed(opts, cb, true), 200);
}
}

// used by specs to ensure the daemon is prewarmed
function ensure(done, shPath = "/runner/prewarm.sh") {
if (!fs.pathExistsSync(statusPath)) {
console.log("Starting daemon with test run to ensure tests run within their allowed time...");
console.log(execSync(`sh ${shPath}`).toString());
done();
}
else {
whenPrewarmed({}, done);
}
}

function clean() {
execSync(`rm -rf ${statusPath}`);
execSync(`gradle --stop`);
}

module.exports = whenPrewarmed;
module.exports.ensure = ensure;
module.exports.clean = clean;

// sets up mocha, also ensures that done never gets called with an argument
module.exports.setupMocha = function() {
before(done => ensure(_ => done()));
after(done => clean(_ => done()));
}
Loading

0 comments on commit 8ba49a6

Please sign in to comment.