[WEB-4533] feat: read replica functionality (#7453)

* feat: read replica functionality

* fix: set use_read_replica to false

* chore: add use_read_replica to external APIs

* chore: remove use_read_replica on read endpoints

* chore: remove md files

* Updated all the necessary endpoints to use read replica

---------

Co-authored-by: Dheeraj Kumar Ketireddy <dheeru0198@gmail.com>
This commit is contained in:
Sangeetha 2025-07-28 17:41:02 +05:30 committed by GitHub
parent b1162395ed
commit 84879ee3bd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 884 additions and 6 deletions

View file

@ -405,6 +405,8 @@ class UserServerAssetEndpoint(BaseAPIView):
class GenericAssetEndpoint(BaseAPIView):
"""This endpoint is used to upload generic assets that can be later bound to entities."""
use_read_replica = True
@asset_docs(
operation_id="get_generic_asset",
summary="Get presigned URL for asset download",

View file

@ -20,6 +20,7 @@ from plane.api.middleware.api_authentication import APIKeyAuthentication
from plane.api.rate_limit import ApiKeyRateThrottle, ServiceTokenRateThrottle
from plane.utils.exception_logger import log_exception
from plane.utils.paginator import BasePaginator
from plane.utils.core.mixins import ReadReplicaControlMixin
class TimezoneMixin:
@ -36,11 +37,15 @@ class TimezoneMixin:
timezone.deactivate()
class BaseAPIView(TimezoneMixin, GenericAPIView, BasePaginator):
class BaseAPIView(
TimezoneMixin, GenericAPIView, ReadReplicaControlMixin, BasePaginator
):
authentication_classes = [APIKeyAuthentication]
permission_classes = [IsAuthenticated]
use_read_replica = False
def filter_queryset(self, queryset):
for backend in list(self.filter_backends):
queryset = backend().filter_queryset(self.request, queryset, self)

View file

@ -86,6 +86,7 @@ class CycleListCreateAPIEndpoint(BaseAPIView):
model = Cycle
webhook_event = "cycle"
permission_classes = [ProjectEntityPermission]
use_read_replica = True
def get_queryset(self):
return (
@ -373,6 +374,7 @@ class CycleDetailAPIEndpoint(BaseAPIView):
model = Cycle
webhook_event = "cycle"
permission_classes = [ProjectEntityPermission]
use_read_replica = True
def get_queryset(self):
return (
@ -633,6 +635,7 @@ class CycleArchiveUnarchiveAPIEndpoint(BaseAPIView):
"""Cycle Archive and Unarchive Endpoint"""
permission_classes = [ProjectEntityPermission]
use_read_replica = True
def get_queryset(self):
return (
@ -831,6 +834,7 @@ class CycleIssueListCreateAPIEndpoint(BaseAPIView):
model = CycleIssue
webhook_event = "cycle_issue"
permission_classes = [ProjectEntityPermission]
use_read_replica = True
def get_queryset(self):
return (
@ -1045,6 +1049,7 @@ class CycleIssueDetailAPIEndpoint(BaseAPIView):
webhook_event = "cycle_issue"
bulk = True
permission_classes = [ProjectEntityPermission]
use_read_replica = True
def get_queryset(self):
return (

View file

@ -51,8 +51,10 @@ class IntakeIssueListCreateAPIEndpoint(BaseAPIView):
"""Intake Work Item List and Create Endpoint"""
serializer_class = IntakeIssueSerializer
model = Intake
permission_classes = [ProjectLitePermission]
use_read_replica = True
def get_queryset(self):
intake = Intake.objects.filter(
@ -214,6 +216,7 @@ class IntakeIssueDetailAPIEndpoint(BaseAPIView):
serializer_class = IntakeIssueSerializer
model = IntakeIssue
use_read_replica = True
filterset_fields = ["status"]

View file

@ -156,6 +156,7 @@ class WorkspaceIssueAPIEndpoint(BaseAPIView):
webhook_event = "issue"
permission_classes = [ProjectEntityPermission]
serializer_class = IssueSerializer
use_read_replica = True
@property
def project_identifier(self):
@ -231,6 +232,7 @@ class IssueListCreateAPIEndpoint(BaseAPIView):
webhook_event = "issue"
permission_classes = [ProjectEntityPermission]
serializer_class = IssueSerializer
use_read_replica = True
def get_queryset(self):
return (
@ -495,6 +497,7 @@ class IssueDetailAPIEndpoint(BaseAPIView):
webhook_event = "issue"
permission_classes = [ProjectEntityPermission]
serializer_class = IssueSerializer
use_read_replica = True
def get_queryset(self):
return (
@ -822,6 +825,7 @@ class LabelListCreateAPIEndpoint(BaseAPIView):
serializer_class = LabelSerializer
model = Label
permission_classes = [ProjectMemberPermission]
use_read_replica = True
def get_queryset(self):
return (
@ -949,6 +953,7 @@ class LabelDetailAPIEndpoint(BaseAPIView):
serializer_class = LabelSerializer
model = Label
permission_classes = [ProjectMemberPermission]
use_read_replica = True
@label_docs(
operation_id="get_labels",
@ -1057,6 +1062,7 @@ class IssueLinkListCreateAPIEndpoint(BaseAPIView):
serializer_class = IssueLinkSerializer
model = IssueLink
permission_classes = [ProjectEntityPermission]
use_read_replica = True
def get_queryset(self):
return (
@ -1163,6 +1169,7 @@ class IssueLinkDetailAPIEndpoint(BaseAPIView):
model = IssueLink
serializer_class = IssueLinkSerializer
use_read_replica = True
def get_queryset(self):
return (
@ -1319,6 +1326,7 @@ class IssueCommentListCreateAPIEndpoint(BaseAPIView):
model = IssueComment
webhook_event = "issue_comment"
permission_classes = [ProjectLitePermission]
use_read_replica = True
def get_queryset(self):
return (
@ -1477,6 +1485,7 @@ class IssueCommentDetailAPIEndpoint(BaseAPIView):
model = IssueComment
webhook_event = "issue_comment"
permission_classes = [ProjectLitePermission]
use_read_replica = True
def get_queryset(self):
return (
@ -1657,6 +1666,7 @@ class IssueCommentDetailAPIEndpoint(BaseAPIView):
class IssueActivityListAPIEndpoint(BaseAPIView):
permission_classes = [ProjectEntityPermission]
use_read_replica = True
@issue_activity_docs(
operation_id="list_work_item_activities",
@ -1712,6 +1722,7 @@ class IssueActivityDetailAPIEndpoint(BaseAPIView):
"""Issue Activity Detail Endpoint"""
permission_classes = [ProjectEntityPermission]
use_read_replica = True
@issue_activity_docs(
operation_id="retrieve_work_item_activity",
@ -1770,6 +1781,7 @@ class IssueAttachmentListCreateAPIEndpoint(BaseAPIView):
serializer_class = IssueAttachmentSerializer
model = FileAsset
permission_classes = [ProjectEntityPermission]
use_read_replica = True
@issue_attachment_docs(
operation_id="create_work_item_attachment",
@ -1977,6 +1989,7 @@ class IssueAttachmentDetailAPIEndpoint(BaseAPIView):
serializer_class = IssueAttachmentSerializer
permission_classes = [ProjectEntityPermission]
model = FileAsset
use_read_replica = True
@issue_attachment_docs(
operation_id="delete_work_item_attachment",
@ -2146,6 +2159,8 @@ class IssueAttachmentDetailAPIEndpoint(BaseAPIView):
class IssueSearchEndpoint(BaseAPIView):
"""Endpoint to search across multiple fields in the issues"""
use_read_replica = True
@extend_schema(
operation_id="search_work_items",
tags=["Work Items"],

View file

@ -24,9 +24,8 @@ from plane.utils.openapi import (
class WorkspaceMemberAPIEndpoint(BaseAPIView):
permission_classes = [
WorkSpaceAdminPermission,
]
permission_classes = [WorkSpaceAdminPermission]
use_read_replica = True
@extend_schema(
operation_id="get_workspace_members",
@ -92,6 +91,7 @@ class WorkspaceMemberAPIEndpoint(BaseAPIView):
# API endpoint to get and insert users inside the workspace
class ProjectMemberAPIEndpoint(BaseAPIView):
permission_classes = [ProjectMemberPermission]
use_read_replica = True
@extend_schema(
operation_id="get_project_members",

View file

@ -80,6 +80,7 @@ class ModuleListCreateAPIEndpoint(BaseAPIView):
model = Module
webhook_event = "module"
permission_classes = [ProjectEntityPermission]
use_read_replica = True
def get_queryset(self):
return (
@ -282,6 +283,7 @@ class ModuleDetailAPIEndpoint(BaseAPIView):
permission_classes = [ProjectEntityPermission]
serializer_class = ModuleSerializer
webhook_event = "module"
use_read_replica = True
def get_queryset(self):
return (
@ -550,6 +552,7 @@ class ModuleIssueListCreateAPIEndpoint(BaseAPIView):
model = ModuleIssue
webhook_event = "module_issue"
permission_classes = [ProjectEntityPermission]
use_read_replica = True
def get_queryset(self):
return (
@ -769,6 +772,7 @@ class ModuleIssueDetailAPIEndpoint(BaseAPIView):
model = ModuleIssue
webhook_event = "module_issue"
bulk = True
use_read_replica = True
permission_classes = [ProjectEntityPermission]
@ -916,6 +920,7 @@ class ModuleIssueDetailAPIEndpoint(BaseAPIView):
class ModuleArchiveUnarchiveAPIEndpoint(BaseAPIView):
permission_classes = [ProjectEntityPermission]
use_read_replica = True
def get_queryset(self):
return (

View file

@ -67,6 +67,7 @@ class ProjectListCreateAPIEndpoint(BaseAPIView):
model = Project
webhook_event = "project"
permission_classes = [ProjectBasePermission]
use_read_replica = True
def get_queryset(self):
return (
@ -331,6 +332,7 @@ class ProjectDetailAPIEndpoint(BaseAPIView):
webhook_event = "project"
permission_classes = [ProjectBasePermission]
use_read_replica = True
def get_queryset(self):
return (

View file

@ -38,6 +38,7 @@ class StateListCreateAPIEndpoint(BaseAPIView):
serializer_class = StateSerializer
model = State
permission_classes = [ProjectEntityPermission]
use_read_replica = True
def get_queryset(self):
return (
@ -164,6 +165,7 @@ class StateDetailAPIEndpoint(BaseAPIView):
serializer_class = StateSerializer
model = State
permission_classes = [ProjectEntityPermission]
use_read_replica = True
def get_queryset(self):
return (