Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle multiple concurrent pip processes on the same environment #2361

Open
mmerickel opened this issue Jan 16, 2015 · 15 comments
Open

Handle multiple concurrent pip processes on the same environment #2361

mmerickel opened this issue Jan 16, 2015 · 15 comments
Labels
type: feature request Request for a new feature

Comments

@mmerickel
Copy link

I saw that pip 6 added file-based locking to the http cache to avoid multi-process contention. Something should be done to handle multiple packages being installed into the same virtualenv at the same time. For example if I had a concurrent makefile that installed independent foo_webapp1 and foo_webapp2 packages with a common dependency on foo_core, what would happen right now? What should happen? I guess the easiest is just to abort the second process.

@pfmoore
Copy link
Member

pfmoore commented Jan 16, 2015

Have you seen incorrect behaviour? I'd tend to assume it's probably OK unless there's evidence otherwise (disclaimer: I haven't looked at the code myself).

@dstufft
Copy link
Member

dstufft commented Jan 16, 2015

My assumption is that it's going to do something wrong, it might end up OK in most scenarios, but in some scenarios it's going to break or do something nonsensical. We should probably have a lock on whatever environment we're currently working on.

@pfmoore
Copy link
Member

pfmoore commented Jan 16, 2015

OK, cool. Concurrency is hard :-)

@mmerickel
Copy link
Author

Mostly pip seems to behave fine (it's not crashing) however I think that if the repository of installed packages is not stable it will be a real problem when the second process attempts to resolve versions, unless at least that part is done in a serial manner.

For example, I ran a test with like 5 concurrent env/bin/pip install -U pyramid together and they each upgraded setuptools individually and nicely handled "Can't uninstall 'setuptools'. No files were found to uninstall." so that seems to work at least.

@xavfernandez xavfernandez changed the title handle multiple pip processes on the same virtualenv Handle multiple concurrent pip processes on the same environment Sep 3, 2015
@xavfernandez
Copy link
Member

As rbtcollins pointed out, it "applies not just to virtualenvs as the other bug does, but to any target environment, including directories, alternate roots, and so on".
And like said in the duplicate #2746, the simple solution would certainly be to take a lock on the environment when performing install/uninstall operations.

@asmodehn
Copy link

Just to mention one simple scenario in that use case, that I previously encountered :

Installing multiple packages concurrently in --editable mode can result in a oneline easy-install.pth containing a random package path, instead of all of the package paths.

alexv@asmodehn:~/tmptest$ git clone https://github.com/pallets/flask.git
Cloning into 'flask'...
remote: Counting objects: 12937, done.
remote: Compressing objects: 100% (8/8), done.
remote: Total 12937 (delta 1), reused 0 (delta 0), pack-reused 12929
Receiving objects: 100% (12937/12937), 3.68 MiB | 794.00 KiB/s, done.
Resolving deltas: 100% (8936/8936), done.
Checking connectivity... done.

alexv@asmodehn:~/tmptest$ git clone https://github.com/kennethreitz/requests.gitCloning into 'requests'...
remote: Counting objects: 19067, done.
remote: Compressing objects: 100% (32/32), done.
remote: Total 19067 (delta 16), reused 0 (delta 0), pack-reused 19035
Receiving objects: 100% (19067/19067), 7.89 MiB | 1.02 MiB/s, done.
Resolving deltas: 100% (12456/12456), done.
Checking connectivity... done.

alexv@asmodehn:~/tmptest$ mkvirtualenv tmptest2
New python executable in /home/alexv/.virtualenvs/tmptest2/bin/python
Installing setuptools, pip, wheel...done.

(tmptest2) alexv@asmodehn:~/tmptest$ pip install -e flask & pip install -e requests
[1] 7883
Obtaining file:///home/alexv/tmptest/requests
Obtaining file:///home/alexv/tmptest/flask
Installing collected packages: requests
  Running setup.py develop for requests
Collecting Werkzeug>=0.7 (from Flask==0.13.dev0)
  Using cached Werkzeug-0.11.15-py2.py3-none-any.whl
Collecting Jinja2>=2.4 (from Flask==0.13.dev0)
  Using cached Jinja2-2.9.4-py2.py3-none-any.whl
Collecting itsdangerous>=0.21 (from Flask==0.13.dev0)
Collecting click>=2.0 (from Flask==0.13.dev0)
  Using cached click-6.7-py2.py3-none-any.whl
Successfully installed requests
(tmptest2) alexv@asmodehn:~/tmptest$ Collecting MarkupSafe>=0.23 (from Jinja2>=2.4->Flask==0.13.dev0)
Installing collected packages: Werkzeug, MarkupSafe, Jinja2, itsdangerous, click, Flask
  Running setup.py develop for Flask
Successfully installed Flask Jinja2-2.9.4 MarkupSafe-0.23 Werkzeug-0.11.15 click-6.7 itsdangerous-0.24

[1]+  Done                    pip install -e flask

(tmptest2) alexv@asmodehn:~/tmptest$ cat ~/.virtualenvs/tmptest2/lib/python2.7/site-packages/easy-install.pth 
/home/alexv/tmptest/requests
/home/alexv/tmptest/flask

(tmptest2) alexv@asmodehn:~/tmptest$ pip install -e flask & pip install -e requests
[1] 7948
Obtaining file:///home/alexv/tmptest/flask
Obtaining file:///home/alexv/tmptest/requests
Requirement already satisfied: Werkzeug>=0.7 in /home/alexv/.virtualenvs/tmptest2/lib/python2.7/site-packages (from Flask==0.13.dev0)
Requirement already satisfied: Jinja2>=2.4 in /home/alexv/.virtualenvs/tmptest2/lib/python2.7/site-packages (from Flask==0.13.dev0)
Requirement already satisfied: itsdangerous>=0.21 in /home/alexv/.virtualenvs/tmptest2/lib/python2.7/site-packages (from Flask==0.13.dev0)
Requirement already satisfied: click>=2.0 in /home/alexv/.virtualenvs/tmptest2/lib/python2.7/site-packages (from Flask==0.13.dev0)
Requirement already satisfied: MarkupSafe>=0.23 in /home/alexv/.virtualenvs/tmptest2/lib/python2.7/site-packages (from Jinja2>=2.4->Flask==0.13.dev0)
Installing collected packages: Flask
  Found existing installation: Flask 0.13.dev0
    Uninstalling Flask-0.13.dev0:
      Successfully uninstalled Flask-0.13.dev0
  Running setup.py develop for Flask
Installing collected packages: requests
  Found existing installation: requests 2.13.0
    Uninstalling requests-2.13.0:
      Successfully uninstalled requests-2.13.0
  Running setup.py develop for requests
Successfully installed Flask
Successfully installed requests
[1]+  Done                    pip install -e flask

(tmptest2) alexv@asmodehn:~/tmptest$ cat ~/.virtualenvs/tmptest2/lib/python2.7/site-packages/easy-install.pth 
/home/alexv/tmptest/requests

(tmptest2) alexv@asmodehn:~/tmptest$ pip install -e flask & pip install -e requests
[1] 7978
Obtaining file:///home/alexv/tmptest/requests
Obtaining file:///home/alexv/tmptest/flask
Installing collected packages: requests
  Found existing installation: requests 2.13.0
    Uninstalling requests-2.13.0:
      Successfully uninstalled requests-2.13.0
  Running setup.py develop for requests
Requirement already satisfied: Werkzeug>=0.7 in /home/alexv/.virtualenvs/tmptest2/lib/python2.7/site-packages (from Flask==0.13.dev0)
Requirement already satisfied: Jinja2>=2.4 in /home/alexv/.virtualenvs/tmptest2/lib/python2.7/site-packages (from Flask==0.13.dev0)
Requirement already satisfied: itsdangerous>=0.21 in /home/alexv/.virtualenvs/tmptest2/lib/python2.7/site-packages (from Flask==0.13.dev0)
Requirement already satisfied: click>=2.0 in /home/alexv/.virtualenvs/tmptest2/lib/python2.7/site-packages (from Flask==0.13.dev0)
Requirement already satisfied: MarkupSafe>=0.23 in /home/alexv/.virtualenvs/tmptest2/lib/python2.7/site-packages (from Jinja2>=2.4->Flask==0.13.dev0)
Installing collected packages: Flask
  Found existing installation: Flask 0.13.dev0
    Uninstalling Flask-0.13.dev0:
      Successfully uninstalled Flask-0.13.dev0
  Running setup.py develop for Flask
Successfully installed requests
Successfully installed Flask

(tmptest2) alexv@asmodehn:~/tmptest$ cat ~/.virtualenvs/tmptest2/lib/python2.7/site-packages/easy-install.pth 
/home/alexv/tmptest/flask
[1]+  Done                    pip install -e flask

and as a result, some package are missing :

(tmptest2) alexv@asmodehn:~/tmptest$ pip list --format=columns
Package      Version   Location                 
------------ --------- -------------------------
appdirs      1.4.0     
click        6.7       
Flask        0.13.dev0 /home/alexv/tmptest/flask
itsdangerous 0.24      
Jinja2       2.9.4     
MarkupSafe   0.23      
packaging    16.8      
pip          9.0.1     
pyparsing    2.1.10    
setuptools   34.0.2    
six          1.10.0    
Werkzeug     0.11.15   
wheel        0.30.0a0  

But let me know if this is worth making a special issue for it...

@asmodehn
Copy link

asmodehn commented Feb 1, 2017

Just a side note on this, in case someone is stuck on the same problem, using setlock seems to fix the issue :

(tmptest) alexv@AlexV-Linux:~$ setlock lockfile pip install -e flask & setlock lockfile pip install -e requests
[1] 23451
Obtaining file:///home/alexv/flask
Requirement already satisfied: Werkzeug>=0.7 in ./.virtualenvs/tmptest/lib/python2.7/site-packages (from Flask==0.13.dev0)
Requirement already satisfied: Jinja2>=2.4 in ./.virtualenvs/tmptest/lib/python2.7/site-packages (from Flask==0.13.dev0)
Requirement already satisfied: itsdangerous>=0.21 in ./.virtualenvs/tmptest/lib/python2.7/site-packages (from Flask==0.13.dev0)
Requirement already satisfied: click>=2.0 in ./.virtualenvs/tmptest/lib/python2.7/site-packages (from Flask==0.13.dev0)
Requirement already satisfied: MarkupSafe>=0.23 in ./.virtualenvs/tmptest/lib/python2.7/site-packages (from Jinja2>=2.4->Flask==0.13.dev0)
Installing collected packages: Flask
  Found existing installation: Flask 0.13.dev0
    Uninstalling Flask-0.13.dev0:
      Successfully uninstalled Flask-0.13.dev0
  Running setup.py develop for Flask
Successfully installed Flask
Obtaining file:///home/alexv/requests
Installing collected packages: requests
  Found existing installation: requests 2.13.0
    Uninstalling requests-2.13.0:
      Successfully uninstalled requests-2.13.0
  Running setup.py develop for requests
Successfully installed requests
[1]+  Done                    setlock lockfile pip install -e flask
(tmptest) alexv@AlexV-Linux:~$ cat ~/.virtualenvs/tmptest/lib/python2.7/site-packages/easy-install.pth 
/home/alexv/flask
/home/alexv/requests

The mutex / queueing behaviour works, however the lockfile remains there after execution. Not sure why, but this doesn't seem to cause any problem so far...

@pradyunsg
Copy link
Member

Would it be enough if there's a single file in a global appdir (pip-running.lock) and if it exists pip would refuse to start properly and abort?

@pradyunsg pradyunsg added type: enhancement Improvements to functionality type: security Has potential security implications labels Aug 21, 2017
@asmodehn
Copy link

This would break usecases where people invoke pip multiple times in parallel, which can make sense when using different virtualenvs, from some kind of script or build framework. It can also happen in the same virtualenv (my usecase) because pip is hooked up with a build system doing things in parallel (make).

Also when someone invoke two pip instances at the same time, I would think the calls should be linearized (one wait for the completion of the other) instead of aborting, to keep whats currently working, working.

@pradyunsg pradyunsg added type: feature request Request for a new feature and removed type: enhancement Improvements to functionality type: security Has potential security implications labels Oct 24, 2017
@amaslenn
Copy link

I think I faced a real issue with multiple pip instances. I'm creating virtual envs in Jenkins workers on the same host, the issue looks like this:

Collecting attrs==19.1.0 (from -r requirements.txt (line 3))
  1 location(s) to search for versions of attrs:
  * https://pypi.org/simple/attrs/
  Getting page https://pypi.org/simple/attrs/
  Found index url https://pypi.org/simple
  Looking up "https://pypi.org/simple/attrs/" in the cache
  Request header has "max_age" as 0, cache bypassed
  https://pypi.org:443 "GET /simple/attrs/ HTTP/1.1" 304 0
  Skipping page https://pypi.org/simple/attrs/ because the GET request got Content-Type: 
  Given no hashes to check 0 links for project 'attrs': discarding no candidates
  ERROR: Could not find a version that satisfies the requirement attrs==19.1.0 (from -r requirements.txt (line 3))

It fails on different packages in different processes, and it works well if the only process started.

$ pip --version
pip 19.2.3

@chrahunt
Copy link
Member

That is the same case as #6970.

@tgamblin
Copy link

tgamblin commented Feb 7, 2020

FYI, if people are interested, we've implemented concurrent locking in Spack at the package level. You can run multiple instances of Spack in parallel, on the same node or on multiple nodes (with most shared filesystems -- provided locks are enabled). The processes will coordinate through fcntl locks (via Python's flock w/reader/writer locks) to build the DAG bottom-up. If one Spack is already running, you can also help it out by launching another to install the same package + dependencies.

See spack/spack#13100. Feel free to steal this stuff -- Spack's Apache-2.0 OR MIT.

jefferyto added a commit to jefferyto/openwrt-packages that referenced this issue Jun 30, 2020
This adds PKG_BUILD_PARALLEL:=0 to packages that depend on host Python
packages (HOST_PYTHON3_PACKAGE_BUILD_DEPENDS), because installing
packages with multiple concurrent pip processes can lead to errors or
unexpected results[1].

This also:

* Move HOST_PYTHON3_PACKAGE_BUILD_DEPENDS definitions to before
  python3-package.mk is included

* Update Python folder readme to include PKG_BUILD_PARALLEL:=0

[1]: pypa/pip#2361

Signed-off-by: Jeffery To <[email protected]>
1715173329 pushed a commit to immortalwrt/packages that referenced this issue Jul 5, 2020
This adds PKG_BUILD_PARALLEL:=0 to packages that depend on host Python
packages (HOST_PYTHON3_PACKAGE_BUILD_DEPENDS), because installing
packages with multiple concurrent pip processes can lead to errors or
unexpected results[1].

This also:

* Move HOST_PYTHON3_PACKAGE_BUILD_DEPENDS definitions to before
  python3-package.mk is included

* Update Python folder readme to include PKG_BUILD_PARALLEL:=0

[1]: pypa/pip#2361

Signed-off-by: Jeffery To <[email protected]>
blocktrron pushed a commit to blocktrron/packages that referenced this issue Jul 22, 2020
This adds PKG_BUILD_PARALLEL:=0 to packages that depend on host Python
packages (HOST_PYTHON3_PACKAGE_BUILD_DEPENDS), because installing
packages with multiple concurrent pip processes can lead to errors or
unexpected results[1].

This also:

* Move HOST_PYTHON3_PACKAGE_BUILD_DEPENDS definitions to before
  python3-package.mk is included

* Update Python folder readme to include PKG_BUILD_PARALLEL:=0

[1]: pypa/pip#2361

Signed-off-by: Jeffery To <[email protected]>
farmergreg pushed a commit to farmergreg/packages that referenced this issue Sep 8, 2020
This adds PKG_BUILD_PARALLEL:=0 to packages that depend on host Python
packages (HOST_PYTHON3_PACKAGE_BUILD_DEPENDS), because installing
packages with multiple concurrent pip processes can lead to errors or
unexpected results[1].

This also:

* Move HOST_PYTHON3_PACKAGE_BUILD_DEPENDS definitions to before
  python3-package.mk is included

* Update Python folder readme to include PKG_BUILD_PARALLEL:=0

[1]: pypa/pip#2361

Signed-off-by: Jeffery To <[email protected]>
farmergreg pushed a commit to farmergreg/packages that referenced this issue Sep 8, 2020
This adds PKG_BUILD_PARALLEL:=0 to packages that depend on host Python
packages (HOST_PYTHON3_PACKAGE_BUILD_DEPENDS), because installing
packages with multiple concurrent pip processes can lead to errors or
unexpected results[1].

This also:

* Move HOST_PYTHON3_PACKAGE_BUILD_DEPENDS definitions to before
  python3-package.mk is included

* Update Python folder readme to include PKG_BUILD_PARALLEL:=0

[1]: pypa/pip#2361

Signed-off-by: Jeffery To <[email protected]>
@emmatyping
Copy link

In mypy we run our test suite in parallel. Some of our tests for PEP 561 require pip to install packages, so we're recently run into some instability in our test suite. Note that each copy of pip is installed in its own venv. Sadly it seems that even with --cache-dir or --no-cache there is some race happening that seems to cause the tests to fail intermittently.

For now we will likely put a lock around package install, but it would be nice if @pradyunsg's suggestion of throwing an error if pip is already running would be implemented, it would certainly have made it easier to discover what was going wrong with our tests :)

What would the next steps be to get to concurrent pip? I imagine a list of all the places pip relies on global state would be a good start.

@pradyunsg
Copy link
Member

pradyunsg commented Jun 6, 2022

Some of our tests for PEP 561 require pip to install packages, so we're recently run into some instability in our test suite. Note that each copy of pip is installed in its own venv.

This should still work. Could you file an issue with the failure logs and a reproducer for the behaviour you're seeing?

Multiple invocations of pip in separate environments should work already. That's literally what pip's own test suite does.

@frant-hartm
Copy link

We are facing this (or a similar) issue in our CI environment:

python3 -m pip install protobuf==3.19.4 grpcio==1.48.0
...
ERROR: Could not find a version that satisfies the requirement protobuf==3.19.4 (from versions: 2.0.0b0, 2.0.3, 2.3.0, 2.4.1, 2.5.0, 2.6.0, 2.6.1, 3.0.0a2, 3.0.0a3, 3.0.0b1, 3.0.0b1.post1, 3.0.0b1.post2, 3.0.0b2, 3.0.0b2.post1, 3.0.0b2.post2, 3.0.0b3, 3.0.0b4, 3.0.0, 3.1.0, 3.1.0.post1, 3.2.0rc1, 3.2.0rc1.post1, 3.2.0rc2, 3.2.0, 3.3.0, 3.4.0, 3.5.0.post1, 3.5.1, 3.5.2, 3.5.2.post1, 3.6.0, 3.6.1, 3.7.0rc2, 3.7.0rc3, 3.7.0, 3.7.1, 3.8.0rc1, 3.8.0, 3.9.0rc1, 3.9.0, 3.9.1, 3.9.2, 3.10.0rc1, 3.10.0, 3.11.0rc1, 3.11.0rc2, 3.11.0, 3.11.1, 3.11.2, 3.11.3, 3.12.0rc1, 3.12.0rc2, 3.12.0, 3.12.1, 3.12.2, 3.12.4, 3.13.0rc3, 3.13.0, 3.14.0rc1, 3.14.0rc2, 3.14.0rc3, 3.14.0, 3.15.0rc1, 3.15.0rc2, 3.15.0, 3.15.1, 3.15.2, 3.15.3, 3.15.4, 3.15.5, 3.15.6, 3.15.7, 3.15.8, 3.16.0rc1, 3.16.0rc2, 3.16.0, 3.17.0rc1, 3.17.0rc2, 3.17.0, 3.17.1, 3.17.2, 3.17.3, 3.18.0rc1, 3.18.0rc2, 3.18.0, 3.18.1, 3.19.0rc1, 3.19.0rc2, 3.19.0, 3.19.1, 4.0.0rc1, 4.0.0rc2)

This happens intermittently. This particular run was with pip-21.3.1.

We have only single pip execution per virtual environment, we do the same for 2 different virtual environments. My understanding is that this should work fine. Any ideas? How do I turn on some debug logging? Could the --system-site-packages which we use for the virtual env have some role here?

frant-hartm added a commit to frant-hartm/hazelcast that referenced this issue Nov 15, 2022
The tests run multiple instances, each instance using a different
virtual environment.

The issue looks similar to this pypa/pip#2361

It seems there are still issues with concurrent pip runs.

Added flock to guard all pip executions.

Fixes hazelcast#22599
frant-hartm added a commit to frant-hartm/hazelcast that referenced this issue Nov 24, 2022
The tests run multiple instances, each instance using a different
virtual environment.

The issue looks similar to this pypa/pip#2361

It seems there are still issues with concurrent pip runs.

Added flock to guard all pip executions.

Fixes hazelcast#22599

Backport of hazelcast#22829
frant-hartm added a commit to hazelcast/hazelcast that referenced this issue Nov 24, 2022
The tests run multiple instances, each instance using a different
virtual environment.

The issue looks similar to this pypa/pip#2361

It seems there are still issues with concurrent pip runs.

Added flock to guard all pip executions.

Fixes #22599
frant-hartm added a commit to hazelcast/hazelcast that referenced this issue Nov 24, 2022
The tests run multiple instances, each instance using a different
virtual environment.

The issue looks similar to this pypa/pip#2361

It seems there are still issues with concurrent pip runs.

Added flock to guard all pip executions.

Fixes #22599

Backport of #22829
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: feature request Request for a new feature
Projects
None yet
Development

No branches or pull requests