You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@openwhisk.apache.org by GitBox <gi...@apache.org> on 2018/08/01 22:23:15 UTC
[GitHub] csantanapr closed pull request #41: remove use of state in nodejs
app
csantanapr closed pull request #41: remove use of state in nodejs app
URL: https://github.com/apache/incubator-openwhisk-runtime-nodejs/pull/41
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git a/core/nodejs6Action/CHANGELOG.md b/core/nodejs6Action/CHANGELOG.md
index b0e7c04..369a5b9 100644
--- a/core/nodejs6Action/CHANGELOG.md
+++ b/core/nodejs6Action/CHANGELOG.md
@@ -19,6 +19,11 @@
# NodeJS 6 OpenWhisk Runtime Container
+## 1.11.0
+Change: Update runtime to work in concurrent mode
+
+- Update runtime to work in concurrent mode [#41](https://github.com/apache/incubator-openwhisk-runtime-nodejs/pull/41/files)
+
## 1.10.0
Change: Update npm openwhisk package from `3.15.0` to `3.16.0`
diff --git a/core/nodejs8Action/CHANGELOG.md b/core/nodejs8Action/CHANGELOG.md
index 6e621d5..9cb6d84 100644
--- a/core/nodejs8Action/CHANGELOG.md
+++ b/core/nodejs8Action/CHANGELOG.md
@@ -19,6 +19,11 @@
# NodeJS 8 OpenWhisk Runtime Container
+## 1.8.0
+Change: Update runtime to work in concurrent mode
+
+- Update runtime to work in concurrent mode [#41](https://github.com/apache/incubator-openwhisk-runtime-nodejs/pull/41/files)
+
## 1.7.0
Change: Update openwhisk npm package from `3.15.0` to `3.16.0`
diff --git a/core/nodejsActionBase/app.js b/core/nodejsActionBase/app.js
index 2da2b89..7dbc433 100644
--- a/core/nodejsActionBase/app.js
+++ b/core/nodejsActionBase/app.js
@@ -17,7 +17,8 @@
var config = {
'port': 8080,
- 'apiHost': process.env.__OW_API_HOST
+ 'apiHost': process.env.__OW_API_HOST,
+ 'allowConcurrent': process.env.__OW_ALLOW_CONCURRENT
};
var bodyParser = require('body-parser');
diff --git a/core/nodejsActionBase/src/service.js b/core/nodejsActionBase/src/service.js
index e1318ee..daf365f 100644
--- a/core/nodejsActionBase/src/service.js
+++ b/core/nodejsActionBase/src/service.js
@@ -27,6 +27,7 @@ function NodeActionService(config) {
};
var status = Status.ready;
+ var ignoreRunStatus = config.allowConcurrent === undefined ? false : config.allowConcurrent.toLowerCase() === "true";
var server = undefined;
var userCodeRunner = undefined;
@@ -112,10 +113,14 @@ function NodeActionService(config) {
*/
this.runCode = function runCode(req) {
if (status === Status.ready) {
- setStatus(Status.running);
+ if (!ignoreRunStatus) {
+ setStatus(Status.running);
+ }
return doRun(req).then(function (result) {
- setStatus(Status.ready);
+ if (!ignoreRunStatus) {
+ setStatus(Status.ready);
+ }
if (typeof result !== "object") {
return errorMessage(502, "The action did not return a dictionary.");
diff --git a/tests/src/test/scala/runtime/actionContainers/NodeJs6ActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/NodeJs6ActionContainerTests.scala
index 78f064e..77fea66 100644
--- a/tests/src/test/scala/runtime/actionContainers/NodeJs6ActionContainerTests.scala
+++ b/tests/src/test/scala/runtime/actionContainers/NodeJs6ActionContainerTests.scala
@@ -21,7 +21,7 @@ import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
@RunWith(classOf[JUnitRunner])
-class NodeJs6ActionContainerTests extends NodeJsActionContainerTests {
+class NodeJs6ActionContainerTests extends NodeJsNonConcurrentTests {
override lazy val nodejsContainerImageName = "nodejs6action"
diff --git a/tests/src/test/scala/runtime/actionContainers/NodeJs8ActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/NodeJs8ActionContainerTests.scala
index ec57018..f4092c6 100644
--- a/tests/src/test/scala/runtime/actionContainers/NodeJs8ActionContainerTests.scala
+++ b/tests/src/test/scala/runtime/actionContainers/NodeJs8ActionContainerTests.scala
@@ -22,7 +22,7 @@ import org.scalatest.junit.JUnitRunner
import spray.json.JsObject
@RunWith(classOf[JUnitRunner])
-class NodeJs8ActionContainerTests extends NodeJsActionContainerTests {
+class NodeJs8ActionContainerTests extends NodeJsNonConcurrentTests {
override lazy val nodejsContainerImageName = "action-nodejs-v8"
diff --git a/tests/src/test/scala/runtime/actionContainers/NodeJsActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/NodeJsActionContainerTests.scala
index a5a136f..3f9694d 100644
--- a/tests/src/test/scala/runtime/actionContainers/NodeJsActionContainerTests.scala
+++ b/tests/src/test/scala/runtime/actionContainers/NodeJsActionContainerTests.scala
@@ -585,4 +585,5 @@ abstract class NodeJsActionContainerTests extends BasicActionRunnerTests with Ws
e shouldBe empty
})
}
+
}
diff --git a/tests/src/test/scala/runtime/actionContainers/NodeJsConcurrentTests.scala b/tests/src/test/scala/runtime/actionContainers/NodeJsConcurrentTests.scala
new file mode 100644
index 0000000..efc294e
--- /dev/null
+++ b/tests/src/test/scala/runtime/actionContainers/NodeJsConcurrentTests.scala
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package runtime.actionContainers
+
+import actionContainers.ActionContainer
+import spray.json.JsObject
+import spray.json.JsString
+
+abstract class NodeJsConcurrentTests extends NodeJsActionContainerTests {
+
+ override lazy val nodejsContainerImageName = "action-nodejs-v8"
+
+ override def withNodeJsContainer(code: ActionContainer => Unit) =
+ withActionContainer(Map("__OW_ALLOW_CONCURRENT" -> "true"))(code)
+
+ it should "allow running activations concurrently" in {
+
+ val requestCount = actorSystem.settings.config.getInt("akka.http.host-connection-pool.max-connections")
+ require(requestCount > 100, "test requires that max-connections be set > 100")
+ println(s"running $requestCount requests")
+
+ val (out, err) = withNodeJsContainer { c =>
+ //this action will create a log entry, and only complete once all activations have arrived and emitted logg
+ //this forces all of the in-action logs to appear in a single portion of the stdout, and all of the sentinels to appear following that
+
+ val code =
+ s"""
+ |
+ | global.count = 0;
+ | let requestCount = $requestCount;
+ | let interval = 1000;
+ | function main(args) {
+ | global.count++;
+ | console.log("interleave me");
+ | return new Promise(function(resolve, reject) {
+ | setTimeout(function() {
+ | checkRequests(args, resolve, reject);
+ | }, interval);
+ | });
+ | }
+ | function checkRequests(args, resolve, reject, elapsed) {
+ | let elapsedTime = elapsed||0;
+ | if (global.count == requestCount) {
+ | resolve({ args: args});
+ | } else {
+ | if (elapsedTime > 30000) {
+ | reject("did not receive "+requestCount+" activations within 30s");
+ | } else {
+ | setTimeout(function() {
+ | checkRequests(args, resolve, reject, elapsedTime+interval);
+ | }, interval);
+ | }
+ | }
+ | }
+ """.stripMargin
+
+ c.init(initPayload(code))._1 should be(200)
+
+ val payloads = (1 to requestCount).map({ i =>
+ JsObject(s"arg$i" -> JsString(s"value$i"))
+ })
+
+ val responses = c.runMultiple(payloads.map {
+ runPayload(_)
+ })
+ payloads.foreach { a =>
+ responses should contain(200, Some(JsObject("args" -> a)))
+ }
+ }
+
+ checkStreams(out, err, {
+ case (o, e) =>
+ o.replaceAll("\n", "") shouldBe "interleave me" * requestCount
+ e shouldBe empty
+ }, requestCount)
+
+ withClue("expected grouping of stdout sentinels") {
+ out should include((ActionContainer.sentinel + "\n") * requestCount)
+ }
+ }
+
+}
diff --git a/tests/src/test/scala/runtime/actionContainers/NodeJsNonConcurrentTests.scala b/tests/src/test/scala/runtime/actionContainers/NodeJsNonConcurrentTests.scala
new file mode 100644
index 0000000..85a4645
--- /dev/null
+++ b/tests/src/test/scala/runtime/actionContainers/NodeJsNonConcurrentTests.scala
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package runtime.actionContainers
+
+import actionContainers.ActionContainer
+import spray.json.JsObject
+import spray.json.JsString
+
+abstract class NodeJsNonConcurrentTests extends NodeJsActionContainerTests {
+
+ override def withNodeJsContainer(code: ActionContainer => Unit) =
+ withActionContainer()(code)
+
+ it should "NOT allow running activations concurrently (without proper env setup)" in {
+ val (out, err) = withNodeJsContainer { c =>
+ //this action will create a log entry, and only complete after 1s, to guarantee previous is still running
+ val code = """
+ | function main(args) {
+ | console.log("no concurrency");
+ | return new Promise(function(resolve, reject) {
+ | setTimeout(function() {
+ | resolve({ args: args});
+ | }, 1000);
+ | });
+ | }
+ """.stripMargin
+
+ c.init(initPayload(code))._1 should be(200)
+ val requestCount = 2
+
+ val payloads = (1 to requestCount).map({ i =>
+ JsObject(s"arg$i" -> JsString(s"value$i"))
+ })
+
+ //run payloads concurrently
+ val responses = c.runMultiple(payloads.map {
+ runPayload(_)
+ })
+
+ //one will fail, one will succeed - currently there is no way to guarantee which one succeeds, since both arrive "at the same time"
+ responses.count {
+ case (200, Some(JsObject(a))) if a.get("args").isDefined => true
+ case _ => false
+ } shouldBe 1
+
+ responses.count {
+ case (403, Some(JsObject(e)))
+ if e.getOrElse("error", JsString("")) == JsString("System not ready, status is running.") =>
+ true
+ case _ => false
+ } shouldBe 1
+
+ }
+
+ checkStreams(out, err, {
+ case (o, e) =>
+ o.replaceAll("\n", "") shouldBe "no concurrency"
+ e shouldBe "Internal system error: System not ready, status is running."
+ }, 1)
+ }
+
+}
diff --git a/tests/src/test/scala/runtime/actionContainers/Nodejs6ConcurrentTests.scala b/tests/src/test/scala/runtime/actionContainers/Nodejs6ConcurrentTests.scala
new file mode 100644
index 0000000..0a218f6
--- /dev/null
+++ b/tests/src/test/scala/runtime/actionContainers/Nodejs6ConcurrentTests.scala
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package runtime.actionContainers
+
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+
+@RunWith(classOf[JUnitRunner])
+class Nodejs6ConcurrentTests extends NodeJsConcurrentTests {
+ override lazy val nodejsContainerImageName = "nodejs6action"
+}
diff --git a/tests/src/test/scala/runtime/actionContainers/Nodejs8ConcurrentTests.scala b/tests/src/test/scala/runtime/actionContainers/Nodejs8ConcurrentTests.scala
new file mode 100644
index 0000000..2f1ffaa
--- /dev/null
+++ b/tests/src/test/scala/runtime/actionContainers/Nodejs8ConcurrentTests.scala
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package runtime.actionContainers
+
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+
+@RunWith(classOf[JUnitRunner])
+class Nodejs8ConcurrentTests extends NodeJsConcurrentTests {
+ override lazy val nodejsContainerImageName = "action-nodejs-v8"
+}
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
With regards,
Apache Git Services