I set up a simple service to receive a binary file. If I send a file of size 10MB, memory_get_usage() reports 115601572; 20MB, 225241104; 30MB, 334880960; 40MB, 444524664, etc. (If I send a 10MB file again, the memory usage will go back down to 115601572.) Every 10MB of base64 file size increase results in ~100MB PHP memory usage increase!

Drupal's XML-RPC facility handles base64 encoding/decoding automatically, so I would expect that maybe the data gets duplicated in memory (plus 1/3x, since base64 requires 4 bytes per 3 bytes binary.) But 10X memory consumption seems like a bug.

Here's the receiver code:

function xml_file_xfer_xmlrpc() {
  return array (
    array (
      'xml_file_xfer.upload',
      'xml_file_xfer_upload',
      array('string','base64','string'),
      t('Upload file via XML-RPC')
    )
  );
}

function xml_file_xfer_upload($data, $filename) {
  return file_save_data($data, $filename);
}

For a client I used a Python script (should be readable to non-Python users):

import xmlrpclib
server = xmlrpclib.Server("http://localhost/sandbox_d6/xmlrpc.php")
testFile = open("testfile",  "rb")
testFileData = xmlrpclib.Binary(testFile.read())
print server.xml_file_xfer.upload(testFileData,"testfile")

I have verified that the file is received correctly and not corrupted in any way.

OS: Ubuntu Linux 8.04
PHP version: 5.2.4-2ubuntu5.2
Web server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.2 with Suhosin-Patch
Drupal version: 6.3

I also tested with a CentOS Linux system as the server and got the same results.

My plan now is to set up a debugger and step through the XML-RPC base64-argument-handling code...

CommentFileSizeAuthor
#1 mem_deltas_trace.txt138.21 KBthreexk

Comments

threexk’s picture

StatusFileSize
new138.21 KB

Traced Drupal's PHP memory usage using xdebug. There are many points where the memory usage increases significantly and several are probably unnecessary. Large memory increases occur between each of these pairs of function calls:

50231372     +276                   -> file_get_contents() /home/threexk/public_html/sandbox_d6/includes/xmlrpcs.inc:56
64396880 +14165508                   -> xmlrpc_message() /home/threexk/public_html/sandbox_d6/includes/xmlrpcs.inc:60

64397836     +120                     -> preg_replace() /home/threexk/public_html/sandbox_d6/includes/xmlrpc.inc:161
92728396 +28330560                     -> trim() /home/threexk/public_html/sandbox_d6/includes/xmlrpc.inc:162

92730360       +0                         -> xmlrpc_message_set() /home/threexk/public_html/sandbox_d6/includes/xmlrpc.inc:225
106895364 +14165004                       -> xmlrpc_message_cdata() /home/threexk/public_html/sandbox_d6/includes/xmlrpc.inc:0

106895364       +0                           -> xmlrpc_message_set() /home/threexk/public_html/sandbox_d6/includes/xmlrpc.inc:202
121060368 +14165004                         -> xmlrpc_message_set() /home/threexk/public_html/sandbox_d6/includes/xmlrpc.inc:231

106895364       +0                         -> trim() /home/threexk/public_html/sandbox_d6/includes/xmlrpc.inc:270
121060368 +14165004                         -> base64_decode() /home/threexk/public_html/sandbox_d6/includes/xmlrpc.inc:270

106895652      +72                         -> func_get_args() /home/threexk/public_html/sandbox_d6/sites/all/modules/services/servers/xmlrpc_server.module:41
117381624 +10485972                         -> services_method_call() /home/threexk/public_html/sandbox_d6/sites/all/modules/services/servers/xmlrpc_server.module:42

Notes:
- This is for a 10MB file transfer (~14MB base64-encoded)
- The +number is a delta in bytes from the previous reading. It is not saying that the call listed on that line caused the increase; it is saying something before that call caused it.
- Several of these memory increases have corresponding decreases (frees). But several don't, which is the problem.
- This is running through the services module, but there is no major memory difference between just using straight XML-RPC.

I was able to cut the 28MB increase down to 14MB by using PHP string functions instead of preg_replace(). This is proof that the memory handling can be improved.

dpearcefl’s picture

Status: Active » Postponed (maintainer needs more info)

Does this issue exist in current D6?

dpearcefl’s picture

Status: Postponed (maintainer needs more info) » Active

Status: Active » Closed (outdated)

Automatically closed because Drupal 6 is no longer supported. If the issue verifiably applies to later versions, please reopen with details and update the version.