Repository: Pyomo/pyomo

Issue:
Timeout in configure_cbc too tight
## Summary

The [initialization code of the CBC solver plugin in `configure_cbc`](https://github.com/Pyomo/pyomo/blob/62cdc7dfd301d044647d8016f70dbe8d79723a97/pyomo/solvers/plugins/solvers/CBCplugin.py#L54-L61) will fail if `cbc -stop` or `cbc dummy -AMPL -stop` takes more than 1 second to return. This can break runs in a non-deterministic way.

### Steps to reproduce the issue

Try to use the CBC solver on a system with lots of load, or a really slow disk. Or make `cbc` a wrapper script that does `sleep 1` before calling the real `cbc`.

Alternatively, you can monkey-patch `subprocess.run` to use a ridiculously short timeout:

```python
import subprocess

def patched_subprocess_run(*args, **kwargs):
    if kwargs.get("timeout") is not None:
        kwargs["timeout"] = 0.001
    return orig_subprocess_run(*args, **kwargs)

orig_subprocess_run = subprocess.run
subprocess.run = patched_subprocess_run

# Your Pyomo call here.
```

### Error Message

```
Failed to create solver with name 'cbc': Command '['/usr/bin/cbc', '-stop']' timed out after 0.001 seconds

RuntimeError: Attempting to use an unavailable solver.
The SolverFactory was unable to create the solver "cbc"
and returned an UnknownSolver object.  This error is raised at the point
where the UnknownSolver object was used as if it were valid (by calling
method "_apply_solver").
```

### Information on your system

Pyomo version: 6.0.1
Python version: 3.8.11
Operating system: Debian Bullseye in Docker
How Pyomo was installed (PyPI, conda, source): pip (PyPI)
Solver (if applicable): CBC

### Additional information

I’m running this in AWS Lambda via a Docker image. The issue occurs from time to time. Maybe it’s related to the Lambda functions being cold, maybe not. In any case, I think a one second timeout is way too short. See also #2080, where a similar issue was reported for the ASL solver.

As a workaround, you can use the monkey-patching method above to set `timeout` to a more sensible value (e.g. `10`). Make sure to use something like `kwargs["timeout"] = max(kwargs["timeout"], 10)` in order to not _decrease_ the timeout.


Discussion:
I have seen this issue crop up on both my local macOS machine as well as in GHA tests on macOS for [Prescient](https://github.com/grid-parity-exchange/Prescient/commit/06c8b1d9416baf1ef85cfa880935674db25487cd) and [Egret](https://github.com/grid-parity-exchange/Egret/commit/592c588fc7485aed6cf5364a25cfadc2bb89eb58#diff-9646e83dd7ca87554dfd7e3a353554049cf852217e350e1833fc23acf2c379c6), where I patched both to execute `cbc -quit` once after cbc is installed but before the tests start. For some reason the `cbc` command becomes cold over time (or if never used).
I have also experienced this bug. Ended up with this code to work around it.


```python
if "AWS_LAMBDA_FUNCTION_NAME" in os.environ:
    unpatched_subprocess_run = subprocess.run

    def patched_subprocess_run(*args: Any, **kwargs: Any) -> Any:
        try:
            if "cbc" in args[0][0] and kwargs.get("timeout") == 1:
                kwargs["timeout"] = 10
        except IndexError:
            pass

        return unpatched_subprocess_run(*args, **kwargs)

    subprocess.run = patched_subprocess_run
```


Resolving patch:
diff --git a/pyomo/solvers/plugins/solvers/CBCplugin.py b/pyomo/solvers/plugins/solvers/CBCplugin.py
--- a/pyomo/solvers/plugins/solvers/CBCplugin.py
+++ b/pyomo/solvers/plugins/solvers/CBCplugin.py
@@ -297,7 +297,7 @@ def _get_version(self):
         """
         results = subprocess.run(
             [self.executable(), "-stop"],
-            timeout=1,
+            timeout=5,
             stdout=subprocess.PIPE,
             stderr=subprocess.STDOUT,
             universal_newlines=True
@@ -310,7 +310,7 @@ def _get_version(self):
     def _compiled_with_asl(self):
         results = subprocess.run(
             [self.executable(), "dummy", "-AMPL", "-stop"],
-            timeout=1,
+            timeout=5,
             stdout=subprocess.PIPE,
             stderr=subprocess.STDOUT,
             universal_newlines=True


Test patch: