43

Complete Working Test Case

Of course depending on your memory on the local and remote machines your array sizes will be different.

z1 = numpy.random.rand(300000000,2);
for i in range(1000):
  print('*******************************************\n'); 
  direct_output = subprocess.check_output('ssh blah@blah "ls /"', shell=True);
  direct_output = 'a'*1200000; 
  a2 = direct_output*10;
  print(len(direct_output));

Current Use Case

In case it helps my use case is as follows:

I issue db queries then store the resulting tables on the remote machine. I then want to transfer them across a network and do analysis. Thus far I have been doing something like the following in python:

#run a bunch of queries before hand with the results in remote files

....
counter = 0
mergedDataFrame = None
while NotDone:
  output = subprocess.check_output('ssh blah@blah cat /data/file%08d'%(counter))
  data = pandas.read_csv(...)
  #do lots of analysis, append, merge, numpy stuff etc...
  mergedDataFrame = pandas.merge(...)
  counter += 1

At some point I receive the following error at the check_output command: [Errno 12] Cannot allocate memory

Background

Thanks to the below questions I think I have an idea of what is wrong. There are a number of solutions posted, and I am trying to determine which of the solutions will avoid the [Errno 12] Cannot allocate memory error associated with the subprocess implementation using fork/clone.

Python subprocess.Popen "OSError: [Errno 12] Cannot allocate memory" This gives the underlying diagnosis and suggests some workaround like spawning separate script etc...

Understanding Python fork and memory allocation errors Suggests using rfoo to circumvent the subprocess limitation of fork/clone and spawning child process and copy memory etc... This seems to imply a client-server model

What is the simplest way to SSH using Python? , but I have the additional constraints that I cannot use subprocess due to memory limitations and fork/clone implementation? The solutions suggests using paramiko or something built on top of it, others suggest subprocess (which I have found will not work in my case).

There were other similar questions but the answers often talked about file descriptors being the culprit (in this case they are not), adding more RAM to the system ( I cannot do this), upgrading to x64 ( I already am on x64). Some hint at the problem of ENOMEM. A few answers mention trying to determine if the subprocess.Popen (in my case check_output) is not properly cleaning the processes, but it looks like S. Lott and others agree that the subprocess code itself is properly cleaning up.

I have searched through the source code on github https://github.com/paramiko/paramiko/search?q=Popen&type=Code and it appears to use subprocess in the proxy.py file.

Actual Questions

Does this mean that ultimately paramiko is using the Popen solution described above that will have problems when the python memory footprint grows and repeated Popen calls are made due to the clone/fork implementation?

If paramiko will not work is there another way to do what I am looking for with a client side only solution? Or will a client/server/socket solution be needed? If so will any of rfoo, tornado, or zeromq, http transfers work here?

Notes I am running 64bit linux 8GB main memory. I do not want to pursue the options of buying more RAM.

Community
  • 1
  • 1
Paul
  • 7,155
  • 8
  • 41
  • 40
  • 1. I can reproduce the issue using [simpler example](https://gist.github.com/7637011). (64bit linux) 2. Normally I'd expect `paramiko` to use sockets to create connections. – jfs Nov 25 '13 at 06:10
  • Have you tried [How to scp in python?](http://stackoverflow.com/q/250283/4279) – jfs Nov 25 '13 at 06:16
  • I did see that link thank you. They seem to mostly suggest paramiko. Paramiko (via sockets) should get around the subprocess module fork/clone issues correct? My main concern (likely erroneous) with paramiko was that I did a quick source code search and saw that it uses the subprocess module- though I don't know what for nor to what extent. – Paul Nov 25 '13 at 13:46
  • Regarding the source code example. Yes your streamlined version will also cause the error. I wanted to put ssh in the example as I think in my case memory on my machine might not be the primary problem as if I do a check_output or some other command locally I don't run into the memory error as quickly. However, it might be that my local box has more memory available so that I don't see the issue as quickly but woudl so if I adjust the array sizes. – Paul Nov 25 '13 at 13:56
  • 2
    Paramiko is a native python ssh implementation. It does not use a subprocess for any part of the transport. – JimB Nov 25 '13 at 19:31
  • thank you JimB for your comments. I will move ahead with paramiko! – Paul Nov 25 '13 at 22:21
  • 1
    related: [Python subprocess.Popen “OSError: [Errno 12\] Cannot allocate memory”](http://stackoverflow.com/q/1367373/4279) – jfs Jun 16 '15 at 00:55

3 Answers3

61

If you are running out of memory, you may want to increase your swap memory. Or you might have no swap enabled at all. In Ubuntu (it should work for other distributions as well) you can check your swap by:

$sudo swapon -s

if it is empty it means you don't have any swap enabled. To add a 1GB swap:

$sudo dd if=/dev/zero of=/swapfile bs=1024 count=1024k
$sudo mkswap /swapfile
$sudo swapon /swapfile

Add the following line to the fstab to make the swap permanent.

$sudo vim /etc/fstab

     /swapfile       none    swap    sw      0       0 

Source and more information can be found here.

Nima
  • 3,129
  • 1
  • 21
  • 20
2

If you're running out of memory, it's probably because subprocess is trying to read too much into memory at the same time. The solution, other than using a redirect to a local file, is probably to use popen-like functionality with an stdin/stdout pair that can be read from a little at a time.

dstromberg
  • 6,954
  • 1
  • 26
  • 27
  • No it is not because of subprocess reading too much into memory. The files are only 200-300MB. Please read the posted links especially the first link to see the issue. It has to do with the way subprocess is implemented: fork/clone. http://stackoverflow.com/questions/1367373/python-subprocess-popen-oserror-errno-12-cannot-allocate-memory – Paul Nov 21 '13 at 04:43
  • Linux fork/clone is copy on write. It should not use a lot of real memory, unless your swap policy doesn't allow overcommit. The main fly in the ointment for this with CPython, is that CPython has reference counts scattered all over the place, which quickly makes pages dirty and need to be copied. – dstromberg Nov 21 '13 at 08:06
  • If you come up with an http://sscce.org/ with appropriate input and expected output, I'd be interested in looking more deeply at this problem. I tried: output = subprocess.check_output(['ssh', 'localhost', 'cat', '/etc/services']) , but it did not replicate the problem. – dstromberg Nov 21 '13 at 08:10
  • What OS are you on? What version of Python are you using? – dstromberg Nov 21 '13 at 08:15
  • If you are on Linux, what is your overcommit policy? https://github.com/torvalds/linux/blob/master/Documentation/sysctl/vm.txt – dstromberg Nov 21 '13 at 08:17
  • How much physmem do you have? How much swap space do you have? – dstromberg Nov 21 '13 at 08:31
  • Physmem is 8GB, swap is 10GB. I am running CentOS 6.4, 64 bit. Python version is 2.7.3 x64. Not sure how to check overcommit, but looking in /proc/sys/vm: memory_failure_recovery: empty nr_overcommit_hugepages: empty overcommit_memory: empty overcommit_ratio: empty – Paul Nov 21 '13 at 12:47
  • It is difficult to get a minimal working example, but I am working on it. In the meantime understanding if paramiko opens one subprocess for the entire life of the object or if it opens a separate subprocess for each remote command run is my biggest question. – Paul Nov 21 '13 at 13:22
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/41642/discussion-between-paul-and-dstromberg) – Paul Nov 21 '13 at 15:17
  • "cat /proc/sys/vm/overcommit_memory" should tell us your overcommit policy. – dstromberg Nov 21 '13 at 15:26
  • I tried to start a chat. Join in here: http://chat.stackoverflow.com/rooms/41642/discussion-between-paul-and-dstromberg. I am starting to determine what is going on. Here is a minimal example: z1 = numpy.random.rand(300000000,2); for i in range(1000): a1 = subprocess.check_output('ssh blah@blah "ls /home/"');#any remote command will do# a1 = 'a'*1200000; a2 = a1*10 #after creating more memory loop again to the remote check output and the remote machine returns the ENOMEM error. So it is the remote machine that is the culprit. Still had to get around this?Will paramiko get around this – Paul Nov 21 '13 at 15:59
0

This should do it:

http://docs.python.org/3.3/library/subprocess.html#replacing-os-popen-os-popen2-os-popen3

That way, you can read lines or blocks, instead of the entire thing at once.

dstromberg
  • 6,954
  • 1
  • 26
  • 27
  • 2
    I am not sure if I am missing something, but I do not want to replace os.popen, I am already using subprocess. It is not the actual file size that is the problem but with the fork/clone in subprocess – Paul Nov 21 '13 at 03:05