aiohttp: rate limiting requests-per-second by domain type - python-3.x

I have already looked here. But still can't get my head around it.
Here is how I am currently accomplishing this:
urls_without_rate_limit =
[
'http://httpbin.org/get'
'http://httpbin.org/get',
'http://httpbin.org/get',
'http://httpbin.org/get',
'http://httpbin.org/get'
]
urls_with_rate_limit =
[
'http://eu.httpbin.org/get'
'http://eu.httpbin.org/get',
'http://eu.httpbin.org/get',
'http://eu.httpbin.org/get',
'http://eu.httpbin.org/get'
]
api_rate = 2
api_limit = 6
loop = asyncio.get_event_loop()
loop.run_until_complete(
process(urls=urls_without_rate_limit, rate=0, limit=len(url_list)))
loop.run_until_complete(
process(urls=urls_with_rate_limit, rate=api_rate, limit=api_limit))
async def process(urls, rate, limit):
limit = asyncio.Semaphore(limit)
f = Fetch(
rate=rate,
limit=limit
)
tasks = []
for url in urls:
tasks.append(f.make_request(url=url))
results = await asyncio.gather(*tasks)
As you can see it will finish the first round of process then start the second round for rate limits.
It works fine but is there a way I could start both rounds at the same time with different rate limits?
tvm

I'll elaborate on what I commented. So you can try to work on you own solution (even though I'll give the complete code here).
You can have a dictionary defining some rules (api -> rate limit per second):
APIS_RATE_LIMIT_PER_S = {
"http://api.mathjs.org/v4?precision=5": 1,
"http://api.mathjs.org/v4?precision=2": 3,
}
Which you can then use to decide which semaphore to pick according to the request URL (in practice you would have to do some parsing to get the endpoints you want to control). Once you have that it's just a matter of using the semaphore to make sure you limit the number of simultaneous process executing your request. The last piece to the puzzle is obviously to add a delay before releasing the semaphore.
I'll get for a different version of what is suggested here, but it's basically the same solution. I just made it so you can modify the session object so each call to session.get will automatically apply rate limit control.
def set_rate_limits(session, apis_rate_limits_per_s):
semaphores = {api: asyncio.Semaphore(s) for api, s in apis_rate_limits_per_s.items()}
#asynccontextmanager
async def limit_rate(url):
await semaphores[url].acquire()
start = time.time()
try:
yield semaphores[url]
finally:
duration = time.time() - start
await asyncio.sleep(1 - duration)
semaphores[url].release()
def add_limit_rate(coroutine):
async def coroutine_with_rate_limit(url, *args, **kwargs):
async with limit_rate(url):
return await coroutine(url, *args, **kwargs)
return coroutine_with_rate_limit
session.get = add_limit_rate(session.get)
session.post = add_limit_rate(session.post)
return session
Notice that using add_limit_rate you could add rate limit control to any coroutine that has an API endpoint as first argument. But here we will just modify session.get and session.post.
In the end you could use the set_rate_limits function like so:
async def main():
apis = APIS_RATE_LIMIT_PER_S.keys()
params = [
{"expr" : "2^2"},
{"expr" : "1/0.999"},
{"expr" : "1/1.001"},
{"expr" : "1*1.001"},
]
async with aiohttp.ClientSession() as session:
session = set_rate_limits(session, APIS_RATE_LIMIT_PER_S)
api_requests = [get_text_result(session, url, params=p) for url, p in product(apis, params)]
text_responses = await asyncio.gather(*api_requests)
print(text_responses)
async def get_text_result(session, url, params=None):
result = await session.get(url, params=params)
return await result.text()
If you run this code you wont see much of what is happening, you could add some print here and there in set_rate_limits to "make sure" the rate limit is correctly enforced:
import time
# [...] change this part :
def add_limit_rate(coroutine):
async def coroutine_with_rate_limit(url, *args, **kwargs):
async with limit_rate(url):
######### debug
global request_count
request_count += 1
this_req_id = request_count
rate_lim = APIS_RATE_LIMIT_PER_S[url]
print(f"request #{this_req_id} -> \t {(time.time() - start)*1000:5.0f}ms \t rate {rate_lim}/s")
########
r = await coroutine(url, *args, **kwargs)
######### debug
print(f"request #{this_req_id} <- \t {(time.time() - start)*1000:5.0f}ms \t rate {rate_lim}/s")
#########
return r
If you run this example asyncio.run(main()), you should get something like:
request #1 -> 1ms rate 1/s
request #2 -> 2ms rate 3/s
request #3 -> 3ms rate 3/s
request #4 -> 3ms rate 3/s
request #1 <- 1003ms rate 1/s
request #2 <- 1004ms rate 3/s
request #3 <- 1004ms rate 3/s
request #5 -> 1004ms rate 1/s
request #6 -> 1005ms rate 3/s
request #4 <- 1006ms rate 3/s
request #5 <- 2007ms rate 1/s
request #6 <- 2007ms rate 3/s
request #7 -> 2007ms rate 1/s
request #7 <- 3008ms rate 1/s
request #8 -> 3008ms rate 1/s
request #8 <- 4010ms rate 1/s
It seems rate limit is respected here, in particular we can have a look at the API with a rate limit of 1 request per second:
request #1 -> 1ms rate 1/s
request #1 <- 1003ms rate 1/s
request #5 -> 1004ms rate 1/s
request #5 <- 2007ms rate 1/s
request #7 -> 2007ms rate 1/s
request #7 <- 3008ms rate 1/s
request #8 -> 3008ms rate 1/s
request #8 <- 4010ms rate 1/s
On the other hand, this solution is not very satisfying as we artificially add a 1s ping to all our requests. This is because of this part of the code:
await asyncio.sleep(1 - duration)
semaphores[url].release()
The problem here is that we are waiting for the sleep to finish before giving out control back to the event loop (scheduling another task, another request). That can easily be solved using this piece of code instead:
asyncio.create_task(release_after_delay(semaphores[url], 1 - duration))
With release_after_delay simply being:
async def release_after_delay(semaphore, delay):
await asyncio.sleep(delay)
semaphore.release()
The asyncio.create_task function makes the coroutine "run this in the background". Which means in this code that the semaphore will be released later, but that we don't need to wait for it to give control back to the even loop (which means some other request can be scheduled and also that we can get the result in add_limit_rate). In other words, we don't care about the result of this coroutine, we just want it to run at some point in the future (which is probably why this function used to be call ensure_future).
Using this patch, we have the following for the API with rate limit set to one request per second:
request #1 -> 1ms rate 1/s
request #1 <- 214ms rate 1/s
request #2 -> 1002ms rate 1/s
request #2 <- 1039ms rate 1/s
request #3 -> 2004ms rate 1/s
request #3 <- 2050ms rate 1/s
request #4 -> 3009ms rate 1/s
request #4 <- 3048ms rate 1/s
It's definitively closer to what we would expect this code to do. We get each response from our API as soon as we can (in this example the ping is 200ms/37ms/46ms/41ms). And the rate limit is respected too.
This is probably not the most beautiful code, but it can be a start for you to work with. Maybe make a clean package with that once you have it working nicely, I guess that's something other people may like to use.

Related

question about combining def function() and PWM duty_ns() in micropython

as a Micro python beginner, I combined few codes found on different forums in order to achieve a higher resolution for ESC signal control. The code will generate from a MIN 1000000 nanoseconds to a MAX 2000000 nanoseconds' pulse but I could only do 100 in increments. My code is kind of messy. Sorry if that hurts your eyes. My question is, does it represent an actual 100ns of resolution? and what's the trick to make it in increments of 1.(Not sure whether is even necessary, but I still hope someone can share some wisdom.)
from machine import Pin, PWM, ADC
from time import sleep
MIN=10000
MAX=20000
class setPin(PWM):
def __init__(self, pin: Pin):
super().__init__(pin)
def duty(self,d):
super().duty_ns(d*100)
print(d*100)
pot = ADC(0)
esc = setPin(Pin(7))
esc.freq(500)
esc.duty(MIN) # arming ESC at 1000 us.
sleep(1)
def map(x, in_min, in_max, out_min, out_max):
return int((x - in_min)*(out_max - out_min)/(in_max - in_min) + out_min)
while True:
pot_val = pot.read_u16()
pulse_ns = map(pot_val, 256, 65535, 10000, 20000)
if pot_val<300: # makes ESC more stable at startup.
esc.duty(MIN)
sleep(0.1)
if pot_val>65300: # gives less tolerance when reaching MAX.
esc.duty(MAX)
sleep(0.1)
else:
esc.duty(pulse_ns) # generates 1000000ns to 2000000ns of pulse.
sleep(0.1)
try to change
esc.freq(500) => esc.freq(250)
x=3600
print (map(3600,256,65535,10000,20000)*100)

Push aggregated metrics to the Prometheus Pushgateway from clustered Node JS

I've ran my node apps in a cluster with Prometheus client implemented. And I've mutated the metrics (for testing purpose) so I've got these result:
# HELP otp_generator_generate_failures A counter counts the number of generate OTP fails.
# TYPE otp_generator_generate_failures counter
otp_generator_generate_failures{priority="high"} 794
# HELP otp_generator_verify_failures A counter counts the number of verify OTP fails.
# TYPE otp_generator_verify_failures counter
otp_generator_verify_failures{priority="high"} 802
# HELP smsblast_failures A counter counts the number of SMS delivery fails.
# TYPE smsblast_failures counter
smsblast_failures{priority="high"} 831
# HELP send_success_calls A counter counts the number of send API success calls.
# TYPE send_success_calls counter
send_success_calls{priority="low"} 847
# HELP send_failure_calls A counter counts the number of send API failure calls.
# TYPE send_failure_calls counter
send_failure_calls{priority="high"} 884
# HELP verify_success_calls A counter counts the number of verify API success calls.
# TYPE verify_success_calls counter
verify_success_calls{priority="low"} 839
# HELP verify_failure_calls A counter counts the number of verify API failure calls.
# TYPE verify_failure_calls counter
verify_failure_calls{priority="high"} 840
FYI: I have adopted the given example from: https://github.com/siimon/prom-client/blob/master/example/cluster.js
Then, I pushed the metrics and everything work fine at the process. But, when I checked the gateway portal, it gives different result from those metrics above.
The one that becomes my opinion is the pushed metrics came from single instance instead of aggregated metrics from few instances running. Isn't it?
So, has anyone ever solved this problem?
Oh, this is my push code:
const { Pushgateway, register } = require('prom-client')
const promPg = new Pushgateway(
config.get('prometheus.pushgateway'),
{
timeout: 5000
},
register
)
promPg.push({ jobName: 'sms-otp-middleware' }, (err, resp, body) => {
console.log(`Error: ${err}`)
console.log(`Body: ${body}`)
console.log(`Response status: ${resp.statusCode}`)
res.status(200).send('metrics_pushed')
})

Azure FaceAPI limits iteration to 20 items

I have a list of image urls from which I use MS Azure faceAPI to extract some features from the photos. The problem is that whenever I iterate more than 20 urls, it seems not to work on any url after the 20th one. There is no error shown. However, when I manually changed the range to iterate the next 20 urls, it worked.
Side note, on free version, MS Azure Face allows only 20 requests/minute; however, even when I let time sleep up to 60s per 10 requests, the problem still persists.
FYI, I have 360,000 urls in total, and sofar I have made only about 1000 requests.
Can anyone help tell me why this happens and how to solve this? Thank you so much!
# My codes
i = 0
for post in list_post[800:1000]:
i += 1
try:
image_url = post['photo_url']
headers = {'Ocp-Apim-Subscription-Key': KEY}
params = {
'returnFaceId': 'true',
'returnFaceLandmarks': 'false',
'returnFaceAttributes': 'age,gender,headPose,smile,facialHair,glasses,emotion,hair,makeup,occlusion,accessories,blur,exposure,noise',
}
response = requests.post(face_api_url, params=params, headers=headers, json={"url": image_url})
post['face_feature'] = response.json()[0]
except (KeyError, IndexError):
continue
if i % 10 == 0:
time.sleep(60)
The free version has a max of 30 000 request per month, your 356 000 faces will therefore take a year to run.
The standard version costs USD 1 per 1000 requests, giving a total cost of USD 360. This option supports 10 transactions per second.
https://azure.microsoft.com/en-au/pricing/details/cognitive-services/face-api/

distributed Tensorflow tracking timestamps for synchronization operations

I am new to TensorFlow. Currently, I am trying to evaluate the performance of distributed TensorFlow using Inception model provided by TensorFlow team.
The thing I want is to generate timestamps for some critical operations in a Parameter Server - Worker architecture, so I can measure the bottleneck (the network lag due to parameter transfer/synchronization or parameter computation cost) on replicas for one iteration (batch).
I came up with the idea of adding a customized dummy py_func operator designated of printing timestamps inside inception_distributed_train.py, with some control dependencies. Here are some pieces of code that I added:
def timer(s):
print ("-------- thread ID ", threading.current_thread().ident, ", ---- Process ID ----- ", getpid(), " ~~~~~~~~~~~~~~~ ", s, datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S.%f'))
return Falsedf
dummy1 = tf.py_func(timer, ["got gradients, before dequeues token "], tf.bool)
dummy2 = tf.py_func(timer, ["finished dequeueing the token "], tf.bool)
I modified
apply_gradients_op = opt.apply_gradients(grads, global_step=global_step)
with tf.control_dependencies([apply_gradients_op]):
train_op = tf.identity(total_loss, name='train_op')
into
with tf.control_dependencies([dummy1]):
apply_gradients_op = opt.apply_gradients(grads, global_step=global_step)
with tf.control_dependencies([apply_gradients_op]):
with tf.control_dependencies([dummy2]):
train_op = tf.identity(total_loss, name='train_op')
hoping to print the timestamps before evaluating the apply_gradient_op and after finishing evaluating the apply_gradient_op by enforcing node dependencies.
I did similar things inside sync_replicas_optimizer.apply_gradients, by adding two dummy print nodes before and after update_op:
dummy1 = py_func(timer, ["---------- before update_op "], tf_bool)
dummy2 = py_func(timer, ["---------- finished update_op "], tf_bool)
# sync_op will be assigned to the same device as the global step.
with ops.device(global_step.device), ops.name_scope(""):
with ops.control_dependencies([dummy1]):
update_op = self._opt.apply_gradients(aggregated_grads_and_vars, global_step)
# Clear all the gradients queues in case there are stale gradients.
clear_queue_ops = []
with ops.control_dependencies([update_op]):
with ops.control_dependencies([dummy2]):
for queue, dev in self._one_element_queue_list:
with ops.device(dev):
stale_grads = queue.dequeue_many(queue.size())
clear_queue_ops.append(stale_grads)
I understand that apply_gradient_op is the train_op returned by sync_replicas_optimizer.apply_gradient. And apply_gradient_op is the op to dequeue a token (global_step) from sync_queue managed by the chief worker using chief_queue_runner, so that replica can exit current batch and start a new batch.
In theory, apply_gradient_op should take some time as replica has to wait before it can dequeue the token (global_step) from sync_queue, but the print result for one replica I got, such as the time differences for executing apply_gradient_op is pretty short (~1/1000 sec) and sometimes the print output is indeterministic (especially for chief worker). Here is a snippet of the output on the workers (I am running 2 workers and 1 PS):
chief worker (worker 0) output
worker 1 output
My questions are:
1) I need to record the time TensorFlow takes to execute an op (such as train_op, apply_gradients_op, compute_gradients_op, etc.)
2) Is this the right direction to go, given my ultimate goal is to record the elapsed time for executing certain operations (such as the difference between the time a replica finishes computing gradients and the time it gets the global_step from sync_token)?
3) If this is not the way it should go, please guide me with some insights about the possible ways I could achieve my ultimate goal.
Thank you so much for reading my long long posts. as I have spent weeks working on this!

Threading: PyQt crashes with "unknown request in queue while dequeuing"

One part of an application I'm developing needs to send some emails to a small group of people. Since it may take a little while to connect to the SMTP server and send the emails, I want to provide a progress bar during this operation using a background thread to do the work.
What happens now is that I can implement a test structure that works just fine, but then as soon as I try to create an object from the backend of my application to actually do any emailing operations, it crashes completely (as though it had segfaulted), dumping this to the console:
[xcb] Unknown request in queue while dequeuing
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
python: ../../src/xcb_io.c:179: dequeue_pending_request: Assertion `!xcb_xlib_unknown_req_in_deq' failed.
Aborted
The only relevant thread I found searching for these errors said something about the signals being implemented wrong (for PySide, PySide and QProgressBar update in a different thread), but in my case the signals work totally fine until I try to create that object (which isn't based on Qt classes at all).
Here's a simplified version of my GUI code:
class SendingDialog(QtGui.QDialog):
def __init__(self, parent, optsDict, cls, zid):
QtGui.QDialog.__init__(self)
self.form = Ui_Dialog()
self.form.setupUi(self)
# initialize some class variables...
self.beginConnect()
self.thread = WorkerThread()
self.thread.insertOptions(self.opts, self.cls, self.zid)
self.thread.finished.connect(self.endOfThread)
self.thread.serverContacted.connect(self.startProgress)
self.thread.aboutToEmail.connect(self.updateProgress)
self.thread.start()
def beginConnect(self):
# start busy indicator
def startProgress(self):
# set up progress bar
def updateProgress(self):
# increment progress bar
def endOfThread(self):
self.thread.quit()
self.reject()
class WorkerThread(QtCore.QThread):
serverContacted = QtCore.pyqtSignal(name="serverContacted")
aboutToEmail = QtCore.pyqtSignal(name="aboutToEmail")
def insertOptions(self, opts, cls, zid):
self.opts = opts
self.cls = cls
self.zid = zid
def run(self):
# upon running the following line, the application crashes.
emailman = db.emailing.EmailManager(self.opts, self.cls, self.zid)
If I put some dummy code into run() that sleeps, emits the appropriate signals, or prints test values, everything works fine; but as soon as I try to instantiate the EmailManager, the whole thing crashes.
EmailManager is an unremarkable class derived from object, taking the parameters I've given it (opts is a dictionary, cls is a different type of similarly unremarkable object, and zid is just a plain number). The constructor looks like this:
def __init__(self, optsDict, cls, zid):
self.opts = optsDict
self.cls = cls
self.historyItem = HistoryItem(zid)
self.studentsList = studentsInClass(cls)
self.connection = None
I'm constructing a couple of other objects based on the parameters, but other than that, nothing complicated or unusual is happening. The code in the db.emailing module does not use Qt or threading at all.
I don't even know how to begin debugging this, so any advice as to what might be going on or how I could try to find out would be very much appreciated.
Edit: In case it's helpful, here's the backtrace from gdb (I don't know enough about what's going on to find it helpful):
Program received signal SIGABRT, Aborted.
[Switching to Thread 0x7fffeb146700 (LWP 31150)]
0x00007ffff762acc9 in __GI_raise (sig=sig#entry=6)
at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
56 ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0 0x00007ffff762acc9 in __GI_raise (sig=sig#entry=6)
at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
#1 0x00007ffff762e0d8 in __GI_abort () at abort.c:89
#2 0x00007ffff7623b86 in __assert_fail_base (
fmt=0x7ffff7774830 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n",
assertion=assertion#entry=0x7ffff6a4420d "!xcb_xlib_unknown_req_in_deq", file=file#entry=0x7ffff6a441db "../../src/xcb_io.c", line=line#entry=179,
function=function#entry=0x7ffff6a446b0 "dequeue_pending_request")
at assert.c:92
#3 0x00007ffff7623c32 in __GI___assert_fail (
assertion=0x7ffff6a4420d "!xcb_xlib_unknown_req_in_deq",
file=0x7ffff6a441db "../../src/xcb_io.c", line=179,
function=0x7ffff6a446b0 "dequeue_pending_request") at assert.c:101
#4 0x00007ffff69d479c in ?? () from /usr/lib/x86_64-linux-gnu/libX11.so.6
#5 0x00007ffff69d55c3 in _XReply ()
from /usr/lib/x86_64-linux-gnu/libX11.so.6
#6 0x00007ffff69bc346 in XGetWindowProperty ()
from /usr/lib/x86_64-linux-gnu/libX11.so.6
#7 0x00007ffff69bb22e in XGetWMHints ()
from /usr/lib/x86_64-linux-gnu/libX11.so.6
#8 0x00007ffff4c87c4b in QWidgetPrivate::setWindowIcon_sys(bool) ()
from /usr/lib/x86_64-linux-gnu/libQtGui.so.4
#9 0x00007ffff4c38405 in QWidget::create(unsigned long, bool, bool) ()
from /usr/lib/x86_64-linux-gnu/libQtGui.so.4
#10 0x00007ffff4c4086a in QWidget::setVisible(bool) ()
from /usr/lib/x86_64-linux-gnu/libQtGui.so.4
#11 0x00007ffff509956e in QDialog::setVisible(bool) ()
from /usr/lib/x86_64-linux-gnu/libQtGui.so.4
#12 0x00007ffff5c24b7c in ?? ()
from /usr/lib/python2.7/dist-packages/PyQt4/QtGui.so
#13 0x00007ffff5099026 in QDialog::exec() ()
from /usr/lib/x86_64-linux-gnu/libQtGui.so.4
#14 0x00007ffff5be5fb5 in ?? ()
from /usr/lib/python2.7/dist-packages/PyQt4/QtGui.so
#15 0x000000000049968d in PyEval_EvalFrameEx ()
#16 0x00000000004a090c in PyEval_EvalCodeEx ()
#17 0x0000000000499a52 in PyEval_EvalFrameEx ()
#18 0x00000000004a1c9a in ?? ()
#19 0x00000000004dfe94 in ?? ()
#20 0x00000000004dc9cb in PyEval_CallObjectWithKeywords ()
#21 0x000000000043734b in PyErr_PrintEx ()
#22 0x00007ffff186fd4d in ?? ()
from /usr/lib/python2.7/dist-packages/sip.so
#23 0x00007ffff14b2ece in ?? ()
from /usr/lib/python2.7/dist-packages/PyQt4/QtCore.so
#24 0x00007ffff45be32f in ?? ()
from /usr/lib/x86_64-linux-gnu/libQtCore.so.4
#25 0x00007ffff79c1182 in start_thread (arg=0x7fffeb146700)
at pthread_create.c:312
#26 0x00007ffff76ee47d in clone ()
at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
Wow, this was obscure.
The X11 windowing functions are apparently not threadsafe unless explicitly set to be so, and for whatever reason PyQt doesn't automatically set them to be. This can be corrected by adding the following before the QApplication constructor:
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads)
See the documentation on QApplicationAttributes.

Resources