Move check for pool capacity. Add comments for further improvements to investigate.
diff --git a/core/dispatcher/src/main/scala/whisk/core/container/ContainerPool.scala b/core/dispatcher/src/main/scala/whisk/core/container/ContainerPool.scala
index ed17ace..8f4af9f 100644
--- a/core/dispatcher/src/main/scala/whisk/core/container/ContainerPool.scala
+++ b/core/dispatcher/src/main/scala/whisk/core/container/ContainerPool.scala
@@ -279,16 +279,20 @@
      */
     def retrieve(key: ActionContainerId)(implicit transid: TransactionId): ContainerResult = {
         this.synchronized {
-            if (activeCount() + startingCounter.cur >= _maxActive) {
-                Busy
-            } else {
-                val bucket = keyMap.getOrElseUpdate(key, new ListBuffer())
-                bucket.find({ ci => ci.isIdle() }) match {
-                    case None => CacheMiss
-                    case Some(ci) => {
-                        ci.state = State.Active
-                        Success(ci.container, None)
+            // first check if there is a matching container and only if there aren't any
+            // determine if the pool is full or has capacity to accomodate a new container;
+            // this allows any new containers introduced into the pool to be reused if already idle            
+            val bucket = keyMap.getOrElseUpdate(key, new ListBuffer())
+            bucket.find({ ci => ci.isIdle() }) match {
+                case None =>
+                    if (activeCount() + startingCounter.cur >= _maxActive) {
+                        Busy
+                    } else {
+                        CacheMiss
                     }
+                case Some(ci) => {
+                    ci.state = State.Active
+                    Success(ci.container, None)
                 }
             }
         }
@@ -324,7 +328,13 @@
                       | startingCounter = ${startingCounter.cur}""".stripMargin)
         // Docker operation outside sync block. Don't pause if we are deleting.
         if (!delete) {
-            runDockerOp { container.pause() }
+            runDockerOp {
+                // pausing eagerly is pessimal; there could be an action waiting
+                // that will immediately unpause the same container to reuse it;
+                // to skip pausing, will need to inspect the queue of waiting activations
+                // for a matching key
+                container.pause()
+            }
         }
 
         val toBeDeleted = this.synchronized { // Return container to pool logically and then optionally delete
@@ -342,6 +352,7 @@
             this.notify()
             toBeDeleted
         }
+
         toBeDeleted.foreach(toBeRemoved.offer(_))
         // Perform capacity-based GC here.
         if (gcOn) { // Synchronization occurs inside calls in a fine-grained manner.
@@ -419,7 +430,8 @@
                 Thread.sleep(100) // serves to prevent busy looping
                 // create a new stem cell if the number of warm containers is less than the count allowed
                 // as long as there is slack so that any actions that may be waiting to create a container
-                // are not held back
+                // are not held back; Note since this method is not fully synchronized, it is possible to
+                // start this operation while there is slack and end up waiting on the docker lock later
                 if (!standalone && getNumberOfIdleContainers(warmNodejsKey) < WARM_NODEJS_CONTAINERS && slack() > 0) {
                     makeWarmNodejsContainer()(tid)
                 }
@@ -474,6 +486,7 @@
         val imageName = WhiskAction.containerImageName(nodejsExec, config.dockerRegistry, config.dockerImagePrefix, config.dockerImageTag)
         val limits = ActionLimits(TimeLimit(), defaultMemoryLimit, LogLimit())
         val containerName = makeContainerName("warmJsContainer")
+        info(this, "Starting warm nodejs container")
         val con = makeGeneralContainer(warmNodejsKey, containerName, imageName, limits, false)
         if (con.containerId.isDefined) {
             this.synchronized {
@@ -505,10 +518,12 @@
         val containerName = makeContainerName(action)
         val con = warmedContainer getOrElse {
             try {
+                info(this, s"making new container because none available")
                 startingCounter.next()
                 makeGeneralContainer(key, containerName, imageName, limits, action.exec.kind == BlackBoxExec)
             } finally {
-                startingCounter.prev()
+                val newCount = startingCounter.prev()
+                info(this, s"made container, number of remaining containers to start: $newCount")
             }
         }
         initWhiskContainer(action, con)
@@ -530,6 +545,9 @@
             ContainerUtils.pullImage(dockerhost, imageName)
         }
         runDockerOp {
+            // because of the docker lock, by the time the container gets around to be started
+            // there could be a container to reuse (from a previous run of the same action, or
+            // from a stem cell container); should revisit this logic
             new WhiskContainer(transid, this, key, containerName, imageName, network, cpuShare, policy, env, limits, isBlackbox = pull)
         }
     }