mirror of
https://git.nadeko.net/Fijxu/invidious.git
synced 2025-12-14 09:05:09 +00:00
feat: Add Video Cache compression
Video information from youtube always weight about ~60KB uncompressed. When using Deflate to compress that information, it gets compressed down to ~15KB, so now you will be able to store up to 4x entries more with caching enabled. For context, nadeko.net Invidious instance stores 54.7k keys, since we also store the `time` in a different key associated to the video ID, the real count of videos cached would be 27.3k. With compression enabled, the Redis database will be able to store up to 4 times more videos in cache, which is 109.2k videos cached. Pretty cool huh.
This commit is contained in:
@@ -1158,6 +1158,13 @@ video_cache:
|
|||||||
##
|
##
|
||||||
lru_max_size: 18432
|
lru_max_size: 18432
|
||||||
|
|
||||||
|
## Compress cache with Deflate to save RAM. Only works with Redis (1) and
|
||||||
|
## LRU (2) cache.
|
||||||
|
##
|
||||||
|
## Accepted values: false, true
|
||||||
|
## Default: false
|
||||||
|
##
|
||||||
|
compress: false
|
||||||
|
|
||||||
##
|
##
|
||||||
## Maximum resolution that is going to be displayed
|
## Maximum resolution that is going to be displayed
|
||||||
|
|||||||
@@ -238,6 +238,8 @@ class Config
|
|||||||
property backend : Int32 = 1
|
property backend : Int32 = 1
|
||||||
# Max quantity of keys that can be held on the LRU cache
|
# Max quantity of keys that can be held on the LRU cache
|
||||||
property lru_max_size : Int32 = 18432 # ~512MB
|
property lru_max_size : Int32 = 18432 # ~512MB
|
||||||
|
# Compress cache with Deflate
|
||||||
|
property compress : Bool = false
|
||||||
end
|
end
|
||||||
|
|
||||||
property check_backends_interval : Int32 = 30
|
property check_backends_interval : Int32 = 30
|
||||||
@@ -453,6 +455,10 @@ class Config
|
|||||||
puts "1 (Redis compatible DB) (Default)"
|
puts "1 (Redis compatible DB) (Default)"
|
||||||
puts "2 (In memory LRU)"
|
puts "2 (In memory LRU)"
|
||||||
end
|
end
|
||||||
|
if config.video_cache.compress && config.video_cache.backend == 0
|
||||||
|
puts "Video Cache compression can only be enabled when using backend 1 (Redis) or 2 (LRU)"
|
||||||
|
exit(1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check if the socket configuration is valid
|
# Check if the socket configuration is valid
|
||||||
|
|||||||
@@ -13,6 +13,37 @@ module Invidious::Database::Videos
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module CacheCompression
|
||||||
|
extend self
|
||||||
|
|
||||||
|
def compress(video_info : String) : String
|
||||||
|
compressed = IO::Memory.new
|
||||||
|
uncompressed = IO::Memory.new
|
||||||
|
uncompressed << video_info
|
||||||
|
uncompressed.rewind
|
||||||
|
Compress::Deflate::Writer.open(compressed, Compress::Deflate::BEST_SPEED) do |deflate|
|
||||||
|
IO.copy(uncompressed, deflate)
|
||||||
|
end
|
||||||
|
compressed.rewind
|
||||||
|
return compressed.gets_to_end
|
||||||
|
end
|
||||||
|
|
||||||
|
def decompress(video_info_compressed : String, id : String) : String?
|
||||||
|
compressed = IO::Memory.new
|
||||||
|
compressed << video_info_compressed
|
||||||
|
compressed.rewind
|
||||||
|
decompressed = Compress::Deflate::Reader.new(compressed, sync_close: true)
|
||||||
|
begin
|
||||||
|
return decompressed.gets_to_end
|
||||||
|
rescue Compress::Deflate::Error
|
||||||
|
# If there is an error when decompressing the video data,
|
||||||
|
# delete the video from the cache to fetch it again.
|
||||||
|
VideoCache.del(id)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class Cache
|
class Cache
|
||||||
def initialize
|
def initialize
|
||||||
case CONFIG.video_cache.backend
|
case CONFIG.video_cache.backend
|
||||||
@@ -68,6 +99,15 @@ module Invidious::Database::Videos
|
|||||||
info = self[id]
|
info = self[id]
|
||||||
time = self[id + ":time"]
|
time = self[id + ":time"]
|
||||||
if info && time
|
if info && time
|
||||||
|
# With the { we identify if it's a JSON or not. In that way, compressed
|
||||||
|
# video info keeps working after setting video_cache.compress to false
|
||||||
|
# and new videos inserted will be uncompressed.
|
||||||
|
if info[0] != '{'
|
||||||
|
info = CacheCompression.decompress(info, id)
|
||||||
|
if info.nil?
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
return Video.new({
|
return Video.new({
|
||||||
id: id,
|
id: id,
|
||||||
info: JSON.parse(info).as_h,
|
info: JSON.parse(info).as_h,
|
||||||
@@ -136,6 +176,13 @@ module Invidious::Database::Videos
|
|||||||
info = @redis.get(id)
|
info = @redis.get(id)
|
||||||
time = @redis.get(id + ":time")
|
time = @redis.get(id + ":time")
|
||||||
if info && time
|
if info && time
|
||||||
|
# With the { we identify if it's a JSON or not
|
||||||
|
if info[0] != '{'
|
||||||
|
info = CacheCompression.decompress(info, id)
|
||||||
|
if info.nil?
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
return Video.new({
|
return Video.new({
|
||||||
id: id,
|
id: id,
|
||||||
info: JSON.parse(info).as_h,
|
info: JSON.parse(info).as_h,
|
||||||
@@ -177,7 +224,19 @@ module Invidious::Database::Videos
|
|||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
return PG_DB.query_one?(request, id, as: Video)
|
data = PG_DB.query_one?(request, id, as: VideoCacheInfo)
|
||||||
|
|
||||||
|
if data
|
||||||
|
if data.info && data.updated
|
||||||
|
return Video.new({
|
||||||
|
id: id,
|
||||||
|
info: JSON.parse(data.info).as_h,
|
||||||
|
updated: Time.parse(data.updated, "%Y-%m-%d %H:%M:%S %z", Time::Location::UTC),
|
||||||
|
})
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -185,7 +244,11 @@ module Invidious::Database::Videos
|
|||||||
extend self
|
extend self
|
||||||
|
|
||||||
def insert(video : Video)
|
def insert(video : Video)
|
||||||
video_cache_info = VideoCacheInfo.new(video.info.to_json, video.id, video.updated.to_s)
|
video_info = video.info.to_json
|
||||||
|
if CONFIG.video_cache.compress
|
||||||
|
video_info = CacheCompression.compress(video_info)
|
||||||
|
end
|
||||||
|
video_cache_info = VideoCacheInfo.new(video_info, video.id, video.updated.to_s)
|
||||||
VideoCache.set(video: video_cache_info, expire_time: 14400) if CONFIG.video_cache.enabled
|
VideoCache.set(video: video_cache_info, expire_time: 14400) if CONFIG.video_cache.enabled
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -194,7 +257,7 @@ module Invidious::Database::Videos
|
|||||||
end
|
end
|
||||||
|
|
||||||
def select(id : String) : Video?
|
def select(id : String) : Video?
|
||||||
return VideoCache.get(id)
|
VideoCache.get(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_expired
|
def delete_expired
|
||||||
|
|||||||
Reference in New Issue
Block a user