Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
24c5a83
<new> Establishing the basic environment
Mar 18, 2023
039e1e0
<fead> DRF's Serializers & Model
Mar 18, 2023
b92df2b
<feat> DRF's Routers
Mar 18, 2023
2d8def3
<chore> Revise the Rate limiting package
Mar 19, 2023
bfa1797
<feat> The header field displays the rate limit judgment
Mar 19, 2023
b0efae7
<test> requirement test && Concurrently test
Mar 21, 2023
7f01ce7
<feat> add sqlite datebase
Mar 22, 2023
957a816
<refactor> view ratelimit_tracking info
Mar 22, 2023
2237f7a
<docs> create README.md && add image
Mar 22, 2023
34eee2b
Update README.md
dirtyrabbit Mar 22, 2023
c6caa76
Update README.md
dirtyrabbit Mar 22, 2023
d99b5d1
Add files via upload
dirtyrabbit Mar 22, 2023
edfb479
Add files via upload
dirtyrabbit Mar 22, 2023
ee4e702
update gitignore
dirtyrabbit Mar 24, 2023
f94670f
Delete webpage.png:Zone.Identifier
dirtyrabbit Mar 24, 2023
f7d9866
update gitgnore
dirtyrabbit Mar 24, 2023
b6694a6
<remove> all file from repo
dirtyrabbit Mar 24, 2023
57ebc56
commit file with gitignore
dirtyrabbit Mar 24, 2023
f744ef6
remove all file
dirtyrabbit Mar 24, 2023
a16c0fa
<fix> commit with gitignore
dirtyrabbit Mar 24, 2023
4bf12a1
<remove> file under data/
dirtyrabbit Mar 24, 2023
8671382
<fix> the count of A mistake
dirtyrabbit Mar 26, 2023
87cb136
<feat> rate limit reset api
dirtyrabbit Mar 26, 2023
8e4f150
<test> add execution time
dirtyrabbit Mar 27, 2023
f237d4f
<feat> add response function and remove tracking function
dirtyrabbit Mar 27, 2023
2b36e0e
Update README.md
dirtyrabbit Mar 27, 2023
fdc7270
<refactor> adjust test about request time of GET
dirtyrabbit Mar 27, 2023
f3bb7c6
<feat> test time greate 1s loop
dirtyrabbit Mar 28, 2023
1703ed7
<test> verify the response header
dirtyrabbit Mar 28, 2023
5f8e886
<fix> git push ignore file
dirtyrabbit Mar 28, 2023
ba9c220
<fix> migrations error
dirtyrabbit Mar 28, 2023
9bba224
<docs> restore files
dirtyrabbit Apr 13, 2023
5e24494
<refactor> change dockerfile copy to add
dirtyrabbit Apr 13, 2023
71b31f6
<refactor> modify tests print
dirtyrabbit Apr 13, 2023
c3fb900
<chore> python 3 to 3.9
dirtyrabbit Apr 13, 2023
39bcb11
<refactor> api reset
dirtyrabbit Apr 13, 2023
c8208e3
<feat> add path field to db
dirtyrabbit Apr 13, 2023
2c4e084
<refactor> change dockerfile ADD to COPY
dirtyrabbit Apr 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .gitgnore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
quiz/01-rate-limit/quiz01/data/
10 changes: 10 additions & 0 deletions quiz/01-rate-limit/quiz01/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# syntax=docker/dockerfile:1
FROM python:3
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /code
COPY requirements.txt /code/
COPY Pytest_test.py /code/
COPY locustfile.py /code/
RUN pip install -r requirements.txt
COPY . /code/
7 changes: 7 additions & 0 deletions quiz/01-rate-limit/quiz01/Pytest_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import pyperf
from core.views import RateLimitAPI
setup = "from core.views import RateLimitAPI"
runner = pyperf.Runner()
runner.timeit(name="Get test",
stmt="RateLimitAPI.get",
setup=setup)
63 changes: 63 additions & 0 deletions quiz/01-rate-limit/quiz01/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# 01-rate-limit
>Rate limiting is used to protect resources from being over-using or abused by users/bots/applications. It is commonly implemented by social media platforms such as Facebook or Instagram.
## The project components
用Django做應用框架搭配 [django-ratelimit](https://django-ratelimit.readthedocs.io/en/stable/)做速率限制,在資料庫上用Sqlite做每個使用者的header file的紀錄。


## Usage
1. 建立Image,tag加入"v3.3.1",或者自行至`docker-compose.yml`修改web的image的賦予名稱

```
docker build -t="v3.3.1" .
```
2. 載入[locustio/locust](https://hub.docker.com/r/locustio/locust),Load testing 的工具
```
docker pull locustio/locust
```

3. 啟動container
```
docker-compose up --scale worker=4
```
4. 開啟網頁,導向 http://localhost:8000/api/

![image](/quiz/01-rate-limit/quiz01/image/webpage.png)


## Test
使用Postman和撰寫Django Test做基本ratelimit測試,也加入[Locust](https://locust.io/)做Load testing 和 Profiling 評估。
### Postman
Postman 是常用的api測試工具,我們可以透過Postman簡單的進行Request,也可以透過該工具清楚的看到response的header field。

**Get**
![image](/quiz/01-rate-limit/quiz01//image/postman_get.png "This is a sample image.")

**Post**
![image](/quiz/01-rate-limit/quiz01//image/postman_post.png "This is a sample image.")

**429 Too Many Requests**
![image](/quiz/01-rate-limit/quiz01//image/postman_429.png "This is a sample image.")

### Django Test
透過Django Test 可以自訂義test的方法,並觀察1秒內的Request count,超過後顯示"Over Rating"。
```
docker-compose run web python3 manage.py test
```
* 自訂義Django Test 4種測試:
1. 在1秒內Request Get 100個請求(Get Not Over)
2. 在1秒內Request Post 1個請求(Post Not Over)
3. 在1秒內Request Get 101個請求(Get Over)
4. 在1秒內Request Post 2個請求(Post Over)

![image](/quiz/01-rate-limit/quiz01//image/django_test.png "This is a sample image.")

### Locust
> [Locust](https://locust.io/)
Define user behaviour with Python code, and swarm your system with millions of simultaneous users.
可以透過Locust簡易的Loading test設定,並觀察測試數據。

![image](/quiz/01-rate-limit/quiz01//image/locust_index.png "This is a sample image.")
![image](/quiz/01-rate-limit/quiz01//image/locust_statistics.png "This is a sample image.")
![image](/quiz/01-rate-limit/quiz01//image/locust_failures.png "This is a sample image.")


Binary file not shown.
Empty file.
3 changes: 3 additions & 0 deletions quiz/01-rate-limit/quiz01/core/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
6 changes: 6 additions & 0 deletions quiz/01-rate-limit/quiz01/core/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class CoreConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'core'
12 changes: 12 additions & 0 deletions quiz/01-rate-limit/quiz01/core/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Generated by Django 3.2.18 on 2023-03-21 18:23

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
]

operations = [
]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the idea of migration file per PR should be consolidated into one single file, not introducing multiple separated files. any particular reason for doing it in this way?

and it seems table HeaderField_Model is used nowhere.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleting and recreating all migrations may cause other issues, such as circular dependencies, depending on how models are constructed. If the goal is to reduce the number of files, we can also use 'Squashing migrations'. However, it should be noted that model interdependencies in Django can get very complex, and squashing may result in migrations that do not run. So if this requirement is deemed necessary or if there are too many files, please let me know again, and we will modify it using this method.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is an open question that are we allow (or recognize) more migration files introduced into a single PR? Think about when multiple people are working on the same repo, will having multiple migrations files would simplify or complex the resolving of conflicts?

Copy link
Author

@dirtyrabbit dirtyrabbit Apr 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is indeed a very common problem, and Django has provided solutions for 'Version control' and this issue.

Regarding the question of

whether to allow (or recognize) more migration files introduced into a single PR,

I don't know much about that. so in my understanding, it is similar to 'rebase' and 'merge'. If there are more complex functionalities to be uploaded, I would prefer 'merge' that has a more complete record. Of course, in general use of 'git', there are different theories on whether to use 'merge' or 'rebase' for branches too.

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Generated by Django 3.2.18 on 2023-03-21 18:28

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
('core', '0001_initial'),
]

operations = [
migrations.CreateModel(
name='Customer_ID_Model',
fields=[
('customer_ID', models.TextField(primary_key=True, serialize=False)),
],
options={
'db_table': 'Customer_ID_Model',
},
),
migrations.CreateModel(
name='HeaderField_Model',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('request_type', models.TextField()),
('Limit', models.IntegerField(default=100)),
('Remaining', models.IntegerField(default=100)),
('Reset', models.IntegerField(default=1)),
('RetryAt', models.IntegerField(default=0)),
('upData', models.DateTimeField(auto_now=True)),
('ID', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.customer_id_model')),
],
options={
'db_table': 'HeaderField_Model',
},
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Generated by Django 3.2.18 on 2023-03-22 04:44

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('core', '0002_customer_id_model_headerfield_model'),
]

operations = [
migrations.CreateModel(
name='GET_Model',
fields=[
('customer_ID', models.TextField(primary_key=True, serialize=False)),
('Limit', models.IntegerField(default=100)),
('Remaining', models.IntegerField(default=100)),
('Reset', models.IntegerField(default=1)),
('RetryAt', models.IntegerField(default=0)),
('upData', models.DateTimeField(auto_now=True)),
],
options={
'db_table': 'GET_Model',
},
),
migrations.CreateModel(
name='POST_Model',
fields=[
('customer_ID', models.TextField(primary_key=True, serialize=False)),
('Limit', models.IntegerField(default=1)),
('Remaining', models.IntegerField(default=1)),
('Reset', models.IntegerField(default=1)),
('RetryAt', models.IntegerField(default=0)),
('upData', models.DateTimeField(auto_now=True)),
],
options={
'db_table': 'POST_Model',
},
),
migrations.RemoveField(
model_name='headerfield_model',
name='ID',
),
migrations.DeleteModel(
name='Customer_ID_Model',
),
migrations.DeleteModel(
name='HeaderField_Model',
),
]
Empty file.
29 changes: 29 additions & 0 deletions quiz/01-rate-limit/quiz01/core/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from django.db import models

# Create your models here.

# class Customer_ID_Model(models.Model):
# customer_ID = models.TextField(primary_key=True)
# # other customer info...
# class Meta:
# db_table = "Customer_ID_Model"

class GET_Model(models.Model):
customer_ID = models.TextField(primary_key=True)
Limit = models.IntegerField(default=100)
Remaining = models.IntegerField(default=100)
Reset = models.IntegerField(default=1)
RetryAt = models.IntegerField(default=0)
upData = models.DateTimeField(auto_now=True)
class Meta:
db_table = "GET_Model"

class POST_Model(models.Model):
customer_ID = models.TextField(primary_key=True)
Limit = models.IntegerField(default=1)
Remaining = models.IntegerField(default=1)
Reset = models.IntegerField(default=1)
RetryAt = models.IntegerField(default=0)
upData = models.DateTimeField(auto_now=True)
class Meta:
db_table = "POST_Model"
47 changes: 47 additions & 0 deletions quiz/01-rate-limit/quiz01/core/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from django.test import TestCase
from django.test import client
import time
# Create your tests here.

class APITestCase(TestCase):

def setUp(self):
self.c = client.Client()

# Unover rate limiting get test
# will be get 200 status
def test_get_not_over(self):
time.sleep(2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

time.sleep(2) is used to clean up the previous state.
however, that means a test runner would take 2 more seconds to be able to complete this test.
are we able to make it act like a state machine? like

serverstub.resetRateLimitConstraint($ip or not)

for i in range(1,101):
            resp = self.c.get('/api/')

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, it would be better to switch to that behavior. However, it's unfortunate that django-ratelimit doesn't seem to provide that functionality directly. Therefore, in the new version, we have created that functionality ourselves, which can be viewed reset function in the 'core/view' . It has also been added to the tests, improving our testing process.
result:

resp = self.c.post('/reset/',{'group':'get', 'key':'ip', 'rate':'100/s', 'method':'GET'}) 

for j in range(1,101):
    resp = self.c.get('/api/') #request api

print('-----------Get not over-----------')
for i in range(1,101):
resp = self.c.get('/api/')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how do you guarantee these 100 API calls would be executed in one second?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In normal circumstances, the testing time will not exceed 1 second, so no special handling is required. In the new version, a judgment of execution time has been added. When the execution time exceeds 1 second, the test will be performed again. If the execution time is less than 1 second or has been tested 5 times (if it cannot be less than 1 second, an error will be displayed), the testing loop will be break or terminated.

for i in range(1,5):  
    t0 = int(time.time())

    // request  //

    t1 = int(time.time())
    if((t1-t0) <1):
        break

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this would trigger infinite testing when the worse scenario has happened. why not just fail the test case?

Copy link
Author

@dirtyrabbit dirtyrabbit Mar 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, could you provide more details about the 'worse scenario'? If you mean that the execution time consistently fails to be less than 1 second, in this case, the loop will be terminated after the 5th iteration, which can be observed from 'for i in range(1,5)'.

def test_get_not_over(self):
        print('-----------Get not over-----------')
        for i in range(1,5):  

            // reset api limit //

            t0 = int(time.time())

            #number of request
            for j in range(1,101):
                 // request api //

            t1 = int(time.time())

            # if execution time < 1, don't do again
            if((t1-t0) <1):
                break

        if((t1-t0) >1):
            self.assertEqual(0, 1) #we can't get it for less than 1s
        else:
            self.assertEqual(resp.status_code, 200)

self.assertEqual(resp.status_code, 200)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

besides http_code, we need to verify the response header too.

Copy link
Author

@dirtyrabbit dirtyrabbit Mar 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem, we can use response["header-name"] to retrieve the header file. However, to keep the testing simple, we separate that test into its own independent one.

# response type of header file is string
# resp['X-RateLimit-Limit'] = '1' resp['X-RateLimit-Remaining'] = '0' resp['X-RateLimit-Reset'] = '0'
self.assertEqual(resp['X-RateLimit-Limit']+resp['X-RateLimit-Remaining']+resp['X-RateLimit-Reset'], '100')
or
self.assertEqual(resp['Retry-At'], '0')



# Unover rate limiting post test
# will be get 200 status
def test_post_not_over(self):
time.sleep(1)
print('-----------Post not over-----------')
resp = self.c.post('/api/')
self.assertEqual(resp.status_code, 200)

# Over rate limiting get test
# will be get 429 status

def test_get_over100(self):
time.sleep(2)
print('-----------Get over-----------')
for i in range(1,102):
resp = self.c.get('/api/')
self.assertEqual(resp.status_code, 429)

# # Over rate limiting post test
# # # will be get 429 status
def test_post_over1(self):
time.sleep(1)
print('-----------Post over-----------')
resp = self.c.post('/api/')
resp = self.c.post('/api/')
self.assertEqual(resp.status_code, 429)

108 changes: 108 additions & 0 deletions quiz/01-rate-limit/quiz01/core/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Create your views here.

# from core.models import CoreModel
from django_ratelimit.decorators import ratelimit
from django.http import HttpResponse,HttpResponseForbidden
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from django.views.generic import View
from django_ratelimit.exceptions import Ratelimited
from django_ratelimit.core import get_usage, is_ratelimited
from core.models import GET_Model, POST_Model

@method_decorator(csrf_exempt, name='dispatch')
class RateLimitAPI(View):

@classmethod
@method_decorator(ratelimit(key='ip', rate='100/s', method='GET'))
def get(cls, request):
block_info = ratelimit_tracking(cls,request,'100/s')
headerfiled_get_db(request,block_info)
response = HttpResponse('X-RateLimit-Limit: '+str(block_info['limit'])+'\n'
+ 'X-RateLimit-Remaining: '+ str(block_info['limit'] - block_info['count'] )+'\n'
+ 'X-RateLimit-Reset: '+ str(block_info['time_left']))
#header filed
response['X-RateLimit-Limit'] = block_info['limit']
response['X-RateLimit-Remaining'] = block_info['limit'] - block_info['count']
response['X-RateLimit-Reset'] = block_info['time_left']
return response

@classmethod
@method_decorator(ratelimit(key='ip', rate='1/s', method='Post'))
def post(cls, request):
block_info = ratelimit_tracking(cls,request,'1/s')
headerfiled_post_db(request,block_info)
response = HttpResponse('X-RateLimit-Limit: '+str(block_info['limit'])+'\n'
+ 'X-RateLimit-Remaining: '+ str(block_info['limit'] - block_info['count'] )+'\n'
+ 'X-RateLimit-Reset: '+ str(block_info['time_left']))
#header filed
response['X-RateLimit-Limit'] = block_info['limit']
response['X-RateLimit-Remaining'] = block_info['limit'] - block_info['count']
response['X-RateLimit-Reset'] = block_info['time_left']
return response

#get ratelimit info
def ratelimit_tracking(fun,request,fun_rate):
block_info = get_usage(request, key="ip",fn=fun, rate=fun_rate,increment =True)
print(block_info)
return block_info

def headerfiled_post_db(request,block_info):
client_id = get_client_ip_address(request)
if POST_Model.objects.filter(customer_ID=client_id).exists():
db_info = POST_Model.objects.filter(customer_ID = client_id)
db_info.update(Limit = block_info['limit'],
Remaining = block_info['limit'] - block_info['count'],
Reset =block_info['time_left'],
RetryAt = block_info['time_left'])

else:
POST_Model.objects.create(customer_ID = client_id,
Limit = block_info['limit'],
Remaining = block_info['limit'] - block_info['count'],
Reset =block_info['time_left'],
RetryAt = block_info['time_left'])

def headerfiled_get_db(request,block_info):
client_id = get_client_ip_address(request)
if GET_Model.objects.filter(customer_ID=client_id).exists():
db_info = GET_Model.objects.filter(customer_ID = client_id)
db_info.update(Limit = block_info['limit'],
Remaining = block_info['limit'] - block_info['count'],
Reset =block_info['time_left'],
RetryAt = block_info['time_left'])

else:
GET_Model.objects.create(customer_ID = client_id,
Limit = block_info['limit'],
Remaining = block_info['limit'] - block_info['count'],
Reset =block_info['time_left'],
RetryAt = block_info['time_left'])

def get_client_ip_address(request):
req_headers = request.META
x_forwarded_for_value = req_headers.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for_value:
ip_addr = x_forwarded_for_value.split(',')[-1].strip()
else:
ip_addr = req_headers.get('REMOTE_ADDR')
return ip_addr

#if over rate limit will redirect 403 to 429
def handler403(request, exception=None):
if isinstance(exception, Ratelimited):
print("Over Rating")
if request.method == 'POST':
block_info = ratelimit_tracking(RateLimitAPI.post,request,'1/s')
elif request.method == 'GET':
block_info = ratelimit_tracking(RateLimitAPI.get,request,'100/s')
headerfiled_get_db(request,block_info)
response = HttpResponse('Too Many Requests'+'\n'
+ 'Retry-At: ' + str(block_info['time_left']), status=429)
response['Retry-At'] = block_info['time_left']
return response
return HttpResponseForbidden('Forbidden')




1 change: 1 addition & 0 deletions quiz/01-rate-limit/quiz01/data/db/PG_VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
15
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/112
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/113
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/1247
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/1249
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/1255
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/1259
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/13370
Binary file not shown.
Binary file not shown.
Binary file not shown.
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Empty file.
Binary file not shown.
Empty file.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/174
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/175
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2187
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2228
Binary file not shown.
Empty file.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2337
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2579
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2600
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2601
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2602
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2603
Binary file not shown.
Binary file not shown.
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2605
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2606
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2607
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2608
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2609
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2610
Binary file not shown.
Binary file not shown.
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2612
Binary file not shown.
Binary file not shown.
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2615
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2616
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2617
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2618
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2619
Binary file not shown.
Binary file not shown.
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2650
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2651
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2652
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2653
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2654
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2655
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2656
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2657
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2658
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2659
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2660
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2661
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2662
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2663
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2664
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2665
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2666
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2667
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2668
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2669
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2670
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2673
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2674
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2675
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2678
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2679
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2680
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2681
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2682
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2683
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2684
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2685
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2686
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2687
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2688
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2689
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2690
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2691
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2692
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2693
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2696
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2699
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2701
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2702
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2703
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2704
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2753
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2754
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2755
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2756
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2757
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2831
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2833
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2835
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2836
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2837
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2838
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2839
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2840
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2841
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/2996
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3079
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3080
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3081
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3085
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3119
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3164
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3257
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3258
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3351
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3379
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3380
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3394
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3395
Binary file not shown.
Empty file.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3431
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3433
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3440
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3455
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3456
Binary file not shown.
Binary file not shown.
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3467
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3468
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3502
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3503
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3534
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3541
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3542
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3574
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3575
Binary file not shown.
Empty file.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3597
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3599
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3600
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3601
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3602
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3603
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3604
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3605
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3606
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3607
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3608
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3609
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3712
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3764
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3766
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3767
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/3997
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/4144
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/4146
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/4148
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/4150
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/4152
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/4154
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/4156
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/4158
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/4160
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/4164
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/4166
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/4168
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/4170
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/4172
Binary file not shown.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/4174
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/5002
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/548
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/549
Binary file not shown.
Empty file.
Empty file.
Empty file.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/6110
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/6111
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/6112
Binary file not shown.
Binary file added quiz/01-rate-limit/quiz01/data/db/base/1/6113
Binary file not shown.
Loading