"""Optional Redis wake-up channel; database remains authoritative job queue. Redis outages degrade to timed polling and never lose durable jobs. A bounded list prevents idle systems accumulating unbounded notification entries. """ import os,socket,ssl,time from urllib.parse import urlparse,unquote URL=os.getenv('SITEFORGE_REDIS_URL','') def _command(sock,*args): payload=b'*'+str(len(args)).encode()+b'\r\n' for arg in args: val=str(arg).encode() payload+=b'$'+str(len(val)).encode()+b'\r\n'+val+b'\r\n' sock.sendall(payload) buf=sock.makefile('rb') line=buf.readline() if not line:raise OSError('Redis disconnected') if line[:1]==b'-':raise OSError(line[1:].decode(errors='replace')) if line[:1] in (b':',b'+'):return line[1:].strip() if line[:1]==b'$': size=int(line[1:].strip()) return buf.read(size+2) if size>=0 else None if line[:1]==b'*': count=int(line[1:].strip()) for _ in range(max(count,0)): n=int(buf.readline()[1:].strip());buf.read(n+2) return count raise OSError('Unexpected Redis response') def _socket(): u=urlparse(URL) if u.scheme not in ('redis','rediss'):raise ValueError('Redis URL requires redis/rediss') s=socket.create_connection((u.hostname,u.port or 6379),timeout=3) s.settimeout(4) if u.scheme=='rediss':s=ssl.create_default_context().wrap_socket(s,server_hostname=u.hostname) if u.password:_command(s,'AUTH',unquote(u.password)) if u.path and u.path not in ('','/','/0'):_command(s,'SELECT',int(u.path.strip('/'))) return s def notify(): if not URL:return False try: with _socket() as s: _command(s,'LPUSH','siteforge:build:wakeup',1) _command(s,'LTRIM','siteforge:build:wakeup',0,1023) return True except (OSError,ValueError,TypeError):return False def wait(fallback=1.5): if not URL: time.sleep(fallback);return try: with _socket() as s:_command(s,'BRPOP','siteforge:build:wakeup',1) except (OSError,ValueError,TypeError):time.sleep(fallback)