Tune performance
- Added threading to gunicorn instance - Changed cache to cachetools's TTL cache since it uses LRU if the cache is full - Increased cache sizes 1.5x
This commit is contained in:
		
							parent
							
								
									5fccffc2f0
								
							
						
					
					
						commit
						e303af090d
					
				| 
						 | 
					@ -100,3 +100,5 @@ $RECYCLE.BIN/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Windows shortcuts
 | 
					# Windows shortcuts
 | 
				
			||||||
*.lnk
 | 
					*.lnk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*.gcloudignore
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
runtime: python38
 | 
					runtime: python38
 | 
				
			||||||
 | 
					
 | 
				
			||||||
entrypoint: gunicorn -b :$PORT twitchrss:app
 | 
					entrypoint: gunicorn -b :$PORT -k gthread --threads 2 twitchrss:app
 | 
				
			||||||
 | 
					
 | 
				
			||||||
env_variables:
 | 
					env_variables:
 | 
				
			||||||
  TWITCH_CLIENT_ID: __INSERT_TWITCH_CLIENT_ID_HERE__
 | 
					  TWITCH_CLIENT_ID: __INSERT_TWITCH_CLIENT_ID_HERE__
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
appdirs==1.4.3
 | 
					appdirs==1.4.3
 | 
				
			||||||
CacheControl==0.12.6
 | 
					CacheControl==0.12.6
 | 
				
			||||||
 | 
					cachetools==4.1.1
 | 
				
			||||||
certifi==2019.11.28
 | 
					certifi==2019.11.28
 | 
				
			||||||
chardet==3.0.4
 | 
					chardet==3.0.4
 | 
				
			||||||
click==7.1.2
 | 
					click==7.1.2
 | 
				
			||||||
| 
						 | 
					@ -7,7 +8,6 @@ colorama==0.4.3
 | 
				
			||||||
contextlib2==0.6.0
 | 
					contextlib2==0.6.0
 | 
				
			||||||
distlib==0.3.0
 | 
					distlib==0.3.0
 | 
				
			||||||
distro==1.4.0
 | 
					distro==1.4.0
 | 
				
			||||||
expiringdict==1.2.1
 | 
					 | 
				
			||||||
Flask==1.1.2
 | 
					Flask==1.1.2
 | 
				
			||||||
gunicorn==20.0.4
 | 
					gunicorn==20.0.4
 | 
				
			||||||
html5lib==1.0.1
 | 
					html5lib==1.0.1
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,14 +22,14 @@ import logging
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
from os import environ
 | 
					from os import environ
 | 
				
			||||||
from feedformatter import Feed
 | 
					from feedformatter import Feed
 | 
				
			||||||
from expiringdict import ExpiringDict
 | 
					from cachetools import cached, TTLCache, LRUCache
 | 
				
			||||||
from io import BytesIO
 | 
					from io import BytesIO
 | 
				
			||||||
import gzip
 | 
					import gzip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VOD_URL_TEMPLATE = 'https://api.twitch.tv/kraken/channels/%s/videos?broadcast_type=archive,highlight,upload&limit=10'
 | 
					VOD_URL_TEMPLATE = 'https://api.twitch.tv/kraken/channels/%s/videos?broadcast_type=archive,highlight,upload&limit=10'
 | 
				
			||||||
USERID_URL_TEMPLATE = 'https://api.twitch.tv/kraken/users?login=%s'
 | 
					USERID_URL_TEMPLATE = 'https://api.twitch.tv/kraken/users?login=%s'
 | 
				
			||||||
VODCACHE_LIFETIME = 600
 | 
					VODCACHE_LIFETIME = 10 * 60
 | 
				
			||||||
USERIDCACHE_LIFETIME = 24 * 60 * 60
 | 
					USERIDCACHE_LIFETIME = 24 * 60 * 60
 | 
				
			||||||
CHANNEL_FILTER = re.compile("^[a-zA-Z0-9_]{2,25}$")
 | 
					CHANNEL_FILTER = re.compile("^[a-zA-Z0-9_]{2,25}$")
 | 
				
			||||||
TWITCH_CLIENT_ID = environ.get("TWITCH_CLIENT_ID")
 | 
					TWITCH_CLIENT_ID = environ.get("TWITCH_CLIENT_ID")
 | 
				
			||||||
| 
						 | 
					@ -41,9 +41,6 @@ if not TWITCH_CLIENT_ID:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
app = Flask(__name__)
 | 
					app = Flask(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
vodcache = ExpiringDict(max_len=200, max_age_seconds=VODCACHE_LIFETIME)
 | 
					 | 
				
			||||||
useridcache = ExpiringDict(max_len=1000, max_age_seconds=USERIDCACHE_LIFETIME)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@app.route('/vod/<string:channel>', methods=['GET', 'HEAD'])
 | 
					@app.route('/vod/<string:channel>', methods=['GET', 'HEAD'])
 | 
				
			||||||
def vod(channel):
 | 
					def vod(channel):
 | 
				
			||||||
| 
						 | 
					@ -63,31 +60,29 @@ def vodonly(channel):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_inner(channel, add_live=True):
 | 
					def get_inner(channel, add_live=True):
 | 
				
			||||||
    userid_json = fetch_userid(channel)
 | 
					    userid_json = fetch_userid(channel)
 | 
				
			||||||
 | 
					    if not userid_json:
 | 
				
			||||||
 | 
					        abort(404)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    (channel_display_name, channel_id) = extract_userid(json.loads(userid_json))
 | 
					    (channel_display_name, channel_id) = extract_userid(json.loads(userid_json))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    channel_json = fetch_vods(channel_id)
 | 
					    channel_json = fetch_vods(channel_id)
 | 
				
			||||||
 | 
					    if not channel_json:
 | 
				
			||||||
 | 
					        abort(404)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    decoded_json = json.loads(channel_json)
 | 
					    decoded_json = json.loads(channel_json)
 | 
				
			||||||
    rss_data = construct_rss(channel, decoded_json, channel_display_name, add_live)
 | 
					    rss_data = construct_rss(channel, decoded_json, channel_display_name, add_live)
 | 
				
			||||||
    headers = {'Content-Type': 'application/rss+xml'}
 | 
					    headers = {'Content-Type': 'application/rss+xml'}
 | 
				
			||||||
    return rss_data, headers
 | 
					    return rss_data, headers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@cached(cache=TTLCache(maxsize=2000, ttl=USERIDCACHE_LIFETIME))
 | 
				
			||||||
def fetch_userid(channel_name):
 | 
					def fetch_userid(channel_name):
 | 
				
			||||||
    return fetch_or_cache_object(channel_name, useridcache, USERID_URL_TEMPLATE)
 | 
					    return fetch_json(channel_name, USERID_URL_TEMPLATE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@cached(cache=TTLCache(maxsize=400, ttl=VODCACHE_LIFETIME))
 | 
				
			||||||
def fetch_vods(channel_id):
 | 
					def fetch_vods(channel_id):
 | 
				
			||||||
    return fetch_or_cache_object(channel_id, vodcache, VOD_URL_TEMPLATE)
 | 
					    return fetch_json(channel_id, VOD_URL_TEMPLATE)
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def fetch_or_cache_object(key, cachedict, url_template):
 | 
					 | 
				
			||||||
    json_data = cachedict.get(key)
 | 
					 | 
				
			||||||
    if not json_data:
 | 
					 | 
				
			||||||
        json_data = fetch_json(key, url_template)
 | 
					 | 
				
			||||||
        if not json_data:
 | 
					 | 
				
			||||||
            abort(404)
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            cachedict[key] = json_data
 | 
					 | 
				
			||||||
    return json_data
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def fetch_json(id, url_template):
 | 
					def fetch_json(id, url_template):
 | 
				
			||||||
| 
						 | 
					@ -112,7 +107,7 @@ def fetch_json(id, url_template):
 | 
				
			||||||
        except Exception as e:
 | 
					        except Exception as e:
 | 
				
			||||||
            logging.warning("Fetch exception caught: %s" % e)
 | 
					            logging.warning("Fetch exception caught: %s" % e)
 | 
				
			||||||
            retries += 1
 | 
					            retries += 1
 | 
				
			||||||
    return None
 | 
					    abort(503)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def extract_userid(user_info):
 | 
					def extract_userid(user_info):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue