3

I have a very simple Python script -- flickr.py -- that uploads a file to Flickr. It looks like this:

#!/usr/bin/python
import sys
import os.path
import flickr_api
KEY = '<key>'
SECRET = '<secret>'
filename = sys.argv[1]
basename = os.path.basename(filename)
flickr_api.set_keys(api_key=KEY, api_secret=SECRET)
flickr_api.set_auth_handler('/home/pi/.flickr/token')
flickr_api.upload(photo_file=filename, title=basename)

Works great. I type flickr photo.jpg and there it is on my Flickr page.

However, I want to throttle the upload speeds so I can run it in the background without affecting other Internet traffic too badly. So I run...

trickle -s -u 10 flickr photo.jpg

... and, sadly, it seems not to throttle the upload speed to 10kbps as I would hope it would. I've tested this with NetHogs open in another window. Goes right up to 80kbps, just as if I had run it without trickle.

I've tried various things -- running it as superuser, prefixing the command with the full path to my Python interpreter (trickle -s -u 10 /usr/bin/python flickr photo.jpg). Nothing seems to work.

I've tested trickle with other programs on this box -- wget, scp -- and it works exactly as expected. What am I missing?

Possibly relevant note: This is on a Raspberry Pi.

UPDATE: The Flickr API module I used is this one...

https://github.com/alexis-mignon/python-flickr-api

... but I just rewrote my example using this one ...

http://stuvel.eu/flickrapi

... with the same results.

hanksims
  • 1,479
  • 2
  • 12
  • 22
  • Maybe it's just me, but I'm not familiar with trickle; is this referring to this app: http://monkey.org/~marius/pages/?page=trickle (note that there is a python lib on pypi also called trickle, but I don't think that's what you're using) – Foon Apr 04 '14 at 02:25
  • Yep, that's the one. Not the pypi/Tornado trickle. – hanksims Apr 04 '14 at 02:31
  • Did you try using a vanilla python socket app to see if it's unique to the flicker app or something about python's interaction with trickle? – Foon Apr 04 '14 at 15:03
  • Yep. I did a `urllib.urlretrieve` behind trickle and it worked fine. – hanksims Apr 04 '14 at 18:17
  • 1
    Looks like trickle relies on library pre-loading to insert itself between the application and the kernel... Maybe your application is bypassing the expected sockets functions? My best guess would be the fact that the Flickr API uses SSL and that the resulting use of OpenSSL upsets the preloading logic somehow. – Peter Brittain Jun 28 '15 at 17:27
  • This may be an extremely silly question - but I note the `trickle` docs talk about KB/s (see usage code [here](https://github.com/mariusae/trickle/blob/master/trickle.c#L165)), which I interpret as kiloBYTES per second. `kbps` is typically kiloBITS per second, so `80 kbps == 10kB / s`. Could that be the problem? If so I'll write an answer for the bounty :). Unrelated - I wonder if @PeterBrittain is the same person of that name who was my boss ~13 years ago. If so, hello! – J Richard Snape Jun 29 '15 at 10:41
  • P.S. Just noticed that the bounty is from non-OP on an old question so pinging @James_pic - see comment above – J Richard Snape Jun 29 '15 at 10:42
  • @JRichardSnape I don't believe it's the bits/bytes confusion, as the differences are too big to account for this - set upload limit to 1kb/s, actual usage is 100kbyte/s, which I don't believe any combination of bit/byte confusion could account for. – James_pic Jun 29 '15 at 10:57
  • @PeterBrittain that was my first thought as well, but my debugging skills aren't up to proving it or fixing it. – James_pic Jun 29 '15 at 10:59
  • @James_pic OK - maybe you could edit on to the OP an example that shows the larger discrepancy as the example in the question does have that factor of 8. As you say - if you put `-u 1` and you actually see 100 kbps that can't be bit/byte confusion – J Richard Snape Jun 29 '15 at 11:17
  • 1
    @James_pic, strace is your friend at this point. For example, see [here](http://stackoverflow.com/questions/5103443/how-to-check-what-shared-library-is-loaded-at-run-time) to find out what shared objects you might be opening. You could also use it to compare what socket calls the working case makes versus the not working case. – Peter Brittain Jun 29 '15 at 11:36
  • @James_pic This is very weird. I set up exactly as per question, using `apt-get trickle`; set up account on flickr etc; using default raspberry pi python install (2.7.3!) did `pip install flickr_api` and "it all just worked (TM)"! I uploaded 1.9MB image in ~100 secs peaking at 13 KB/s (brief) stabilising at 10.081. It's not that I'm on a slow network - without trickle, it uploaded in about 3 seconds peaking at 400 KB/s. Measuring speeds with [nethogs](http://nethogs.sourceforge.net/). Can you say any more about use case - image size, versions etc. It can't be flickr specific... – J Richard Snape Jun 29 '15 at 13:38
  • In the interests of investigation - I also ran both with and without trickle under `strace` and diffed the results. As expected - there are a few more `open` lines in the `trickle` version, as `libbsd.so.0` and `/libc.so.6` are opened immediately prior to opening `trickle-overload.so` and again afterward. Otherwise, the files are identical. Note that this is all on Raspberry Pi - Raspbian OS. I'm becoming more convinced the OP problem is the bits/bytes. Maybe you have a different problem, given the factor of 100 - please do add some more info. – J Richard Snape Jun 29 '15 at 13:53
  • @JRichardSnape My apologies. It seems I added the bounty without validating that the OP's specific problem was reproducible. My problem was using the Amazon awscli tool (uses Python 3.4 under the hood, and ultimately delegates to HTTPConnection) to upload large files, and I assumed it had the same root cause. I'll see if I can come up with a more concrete way to reproduce. – James_pic Jun 29 '15 at 13:55
  • @James_pic OK - easy mistake to make - I'll leave it there for now as I've spent a fair amount of time setting up the system to (not!) repro OP problem already. Ping me if you come up with an easily reproducible example. I don't know what will happen for the bounty - maybe I'll put an answer that presents my investigation so far, hypothesis on OP problem and any info we can gather on yours if you are not allowed to rescind it. – J Richard Snape Jun 29 '15 at 13:59
  • @JRichardSnape I don't think it's possible to rescind a bounty, but if I can't produce a good test case (e.g, it turns out to be some weird quirk of my local setup), a good answer around how you'd use strace to understand what's going on would seem bounty-worthy. – James_pic Jun 29 '15 at 14:02

2 Answers2

2

TL;DR

The OP problem couldn't be reproduced on identical hardware, thus I believe it to be a misreading of units (10 KB/s == 80 kbps). A similar but not quite the same problem prompted a bounty on the question, so I have offered some diagnostic techniques for that. I've broken the response into sections:

  1. Original problem
  2. Diagnostic step suggestions
  3. Hypothesis of what may be the problem using awscli

1. Original problem (non-)reproduction

I set up a Raspberry Pi using the Raspbian OS (no GUI) with exactly the same libraries as the OP (all done with sudo for speed...)

sudo pip install flickr_api
sudo apt-get install nethogs
sudo apt-get trickle

You then have to register for a Flickr account, create a (non-commercial) app and set up the API keys etc - instructions for app creation here and api key acquisition here or, via the Python wrapper, here.

I found a 1.9MB image, named it test.jpg and tried the following:

  1. /usr/bin/python flickr.py test.jpg
  2. trickle -s -u 10 /usr/bin/python flickr.py photo.jpg

Results

  1. the picture uploaded in ~3 secs, with nethogs showing a peak upload speed ~400KB/s
  2. the picture took well over a minute to upload and nethogs showed upload rate peaking at 13 KB/s (briefly) before stabilising at 10.081.

Everything working as expected

Thus, I believe the OP issue is as simple as this: the upload limit in trickle is set in kilobytes per second (kB/s), and I think that the OP was probably reading the speed in kilobits per second (kbps). Hence the factor of 8.


2. However...Diagnostics for similar problems

@James_pic posted a bounty on this question and it turns out that his scenario is not identical to the OP. In particular, he is observing no limiting when uploading to Amazon Web services using aws-cli

This being the case, I'm going to post some further diagnostic methods. The paper describing how trickle works is here and describes a couple of scenarios where trickle won't work:

  1. When the user does not voluntarily run under trickle
  2. Secondly and with smaller impact, Trickle cannot work with statically linked binaries.

It is possible that 2 is relevant here, so I'll outline a method you might start to diagnose that using strace. There is another question on this site covering strace usage in general and specific usage to look for shared libraries in use here.

As I haven't got a reproducible example with aws-cli, I'll show what I did to verify operation in the flickr_api case.

From the man page, strace

intercepts and records the system calls which are called by a process and the signals which are received by a process. As we are investigating which system libraries are in use, we are interested in the open messages, so I used the following, in effect "wrapping" the commands from above with strace and then grepping the output for open:

  1. strace /usr/bin/python flickr.py test.jpg 2>&1 | grep open > strace.out
  2. strace trickle -s -u 10 /usr/bin/python flickr.py test.jpg | grep open > strace_with_trickle.out

Finally, I ran a diff of these two files:

diff strace_with_trickle.out strace.out

Unsurprisingly, the version with trickle output a few more lines - specifically these:

< open("/lib/arm-linux-gnueabihf/libbsd.so.0", O_RDONLY) = 3
< open("/lib/arm-linux-gnueabihf/libc.so.6", O_RDONLY) = 3
< open("/lib/arm-linux-gnueabihf/libgcc_s.so.1", O_RDONLY) = 3
< open("/usr/lib/trickle/trickle-overload.so", O_RDONLY) = 3
< open("/etc/ld.so.preload", O_RDONLY)    = 3
< open("/usr/lib/arm-linux-gnueabihf/libcofi_rpi.so", O_RDONLY) = 3
< open("/etc/ld.so.cache", O_RDONLY)      = 3

These are the lines covering the interpositioning of trickle-overload.so when the process is run. In particular, we can observe that the standard socket libraries are in use and dynamically loaded here - i.e. libc.so.6 adn libbsd.so.0, giving some confidence that trickle should work.

This technique is quite generic - use of a working case and a non-working case and then diffing the outputs should get you a lot further along the road.


3. Finally ... awscli hypothesis

I have a suspicion that awscli might spawn new processes as part of its uploading. If this is the case, I think trickle with the -s option will not limit those child processes. You can use it in daemon mode - this might work better - you can see how to do this by typing man trickled

In essence - you first start the daemon e.g.

trickled -u 10

and then "subscribe" processes to it e.g.

trickle /usr/bin/python flickr.py test.jpg

Caveat I have no reproducible test case for the awscli problem, so this is speculation.

Community
  • 1
  • 1
J Richard Snape
  • 20,116
  • 5
  • 51
  • 79
0

Which distro are you using? I know Ubuntu's (18.04 and 20.04 at least) package of trickle is missing some upstream fixes, e.g. https://github.com/mariusae/trickle/commit/bb2825a1fe938e303acd5d65508476ca03961d85

I'd suggest you compile trickle from source and retry.

This commit in particular will fix issues with python3. Python2 apparently doesn't use SOCK_CLOEXEC or SOCK_NONBLOCK, so it's not impacted.

Edwin
  • 161
  • 1
  • 1
  • 5