This repository has been archived by the owner on Jan 8, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16 from zteeed/add_redis_stream
Add redis stream
- Loading branch information
Showing
25 changed files
with
330 additions
and
129 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import aioredis | ||
|
||
from api.constants import REDIS_STREAM_USERS, REDIS_STREAM_CHALLENGES, CONSUMER_GROUP_NAME | ||
|
||
|
||
async def add_stream_to_consumer_group(redis_app: aioredis.Redis, stream: str, group_name: str, latest_id: str = '$', | ||
mkstream: bool = False): | ||
# aioredis==1.2.0 install via pip does not support mkstream option on xgroup_create (see github repository) | ||
args = [b'CREATE', stream, group_name, latest_id] | ||
if mkstream: | ||
args.append(b'MKSTREAM') | ||
await redis_app.execute(b'XGROUP', *args) | ||
|
||
|
||
async def create_consumer_group(redis_app: aioredis.Redis): | ||
for stream in [REDIS_STREAM_CHALLENGES, REDIS_STREAM_USERS]: | ||
try: | ||
await add_stream_to_consumer_group(redis_app, stream, CONSUMER_GROUP_NAME, mkstream=True) | ||
except aioredis.errors.ReplyError as exception: | ||
if 'BUSYGROUP Consumer Group name already exists' != str(exception): | ||
raise exception |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import json | ||
from datetime import datetime | ||
from typing import Optional | ||
from aioredis.commands import Redis | ||
|
||
from api.constants import REDIS_STREAM_CHALLENGES, REDIS_STREAM_USERS, REQUEST_TIMEOUT, UPDATE_TIMEOUT | ||
|
||
|
||
def get_timeout(handler_type: str) -> int: | ||
if handler_type == 'dynamic_user': | ||
return UPDATE_TIMEOUT | ||
else: | ||
return 10 * UPDATE_TIMEOUT | ||
|
||
|
||
def extract_timestamp_last_update(data: str) -> Optional[datetime]: | ||
if data is None: | ||
return | ||
data = json.loads(data) | ||
if 'last_update' not in data.keys(): | ||
return | ||
return datetime.fromisoformat(data['last_update']) | ||
|
||
|
||
async def send_tasks_to_worker(redis_app: Redis, arg: Optional[str], now: datetime, timestamp: Optional[datetime], | ||
timeout: int, handler_type: str) -> None: | ||
# updates conditions | ||
condition = timestamp is None or (now - timestamp).total_seconds() > timeout | ||
|
||
# make updates (send tasks to worker) | ||
if handler_type == 'static' and condition: | ||
await redis_app.xadd(REDIS_STREAM_CHALLENGES, {b'update': b"ok"}) | ||
elif handler_type == 'dynamic_user' and arg is not None and condition: | ||
await redis_app.xadd(REDIS_STREAM_USERS, {b'username': arg.encode()}) | ||
elif handler_type == 'dynamic_categories' and arg is not None and condition: | ||
await redis_app.xadd(REDIS_STREAM_CHALLENGES, {b'update': b"ok"}) | ||
|
||
|
||
def need_waiting_for_update(now: datetime, timestamp: Optional[datetime], timeout: int) -> bool: | ||
return timestamp is None or (now - timestamp).total_seconds() > 2*timeout | ||
|
||
|
||
async def force_update(redis_app: Redis, key: str, now: datetime, timestamp: Optional[datetime], timeout: int) -> None: | ||
condition = timestamp is None or (now - timestamp).total_seconds() > timeout | ||
while condition and abs(now-datetime.now()).total_seconds() < REQUEST_TIMEOUT: | ||
data = await redis_app.get(f'{key}') | ||
timestamp = extract_timestamp_last_update(data) | ||
condition = timestamp is None or (now - timestamp).total_seconds() > timeout | ||
return | ||
|
||
|
||
async def read_from_redis_key(redis_app: Redis, key: str, arg: Optional[str], handler_type: str = 'static'): | ||
data = await redis_app.get(f'{key}') | ||
now = datetime.now() | ||
timestamp = extract_timestamp_last_update(data) | ||
timeout = get_timeout(handler_type) | ||
|
||
await send_tasks_to_worker(redis_app, arg, now, timestamp, timeout, handler_type) | ||
response = need_waiting_for_update(now, timestamp, timeout) | ||
if response: | ||
await force_update(redis_app, key, now, timestamp, timeout) | ||
|
||
return await redis_app.get(f'{key}') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
redis | ||
aioredis | ||
hiredis | ||
requests | ||
lxml | ||
structlog | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,11 @@ | ||
from structlog import get_logger | ||
|
||
|
||
class App: | ||
|
||
def __init__(self): | ||
self.redis = None | ||
|
||
|
||
app = App() | ||
log = get_logger() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
URL = 'https://www.root-me.org/' | ||
AUTHORS = ["Aurélien Duboc", "Nicolas Bonnet"] | ||
GITHUB_ACCOUNTS = ["https://github.com/zteeed", "https://github.com/bonnetn"] | ||
REDIS_STREAM_USERS = 'update_users' | ||
REDIS_STREAM_CHALLENGES = 'update_challenges' | ||
CG_NAME = 'rootme' | ||
CONSUMER_NAME = 'worker1' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.