Reducing polling load with ETag and conditional requests
Conditional requests with ETag.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # ====================================================================== # Q: Thousands of players ask every few minutes "has anything changed?". How do # you reduce the load? # # Conditional requests with ETag. The player sends If-None-Match, and if nothing # has changed I answer 304 with no body. I derive the ETag from a cheap field # (e.g. the playlist's updated_at or content_version), so I don't need to build # the whole response to compute it. It can also be kept in Redis so it doesn't # even hit the database. # Alternatively, push instead of poll: WebSocket (Django Channels) or MQTT, # where the server notifies only when there is a change. You usually keep a poll # with a long interval as a fallback too, because screen connections drop. # ====================================================================== class PlayerManifestView(APIView): authentication_classes = [DeviceTokenAuthentication] def get(self, request): screen = request.auth.screen etag = f'"{screen.playlist_id}-{screen.playlist.content_version}"' if request.headers.get('If-None-Match') == etag: return Response(status=304) data = build_manifest(screen) # the heavy part return Response(data, headers={'ETag': etag, 'Cache-Control': 'private, no-cache'}) |