Coverage for app/backend/src/couchers/server.py: 93%

88 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-07-22 18:39 +0000

1from concurrent import futures 

2from typing import Any 

3 

4import grpc 

5 

6from couchers.config import config 

7from couchers.constants import SERVER_THREADS 

8from couchers.db import _get_base_engine 

9from couchers.interceptors import ( 

10 CouchersMiddlewareInterceptor, 

11 ErrorSanitizationInterceptor, 

12 OTelInterceptor, 

13) 

14from couchers.metrics import grpc_in_flight_gauge, start_worker_resource_sampler 

15from couchers.proto import ( 

16 account_pb2_grpc, 

17 admin_pb2_grpc, 

18 api_pb2_grpc, 

19 auth_pb2_grpc, 

20 blocking_pb2_grpc, 

21 bugs_pb2_grpc, 

22 communities_pb2_grpc, 

23 conversations_pb2_grpc, 

24 dashboard_pb2_grpc, 

25 discussions_pb2_grpc, 

26 donations_pb2_grpc, 

27 editor_pb2_grpc, 

28 events_pb2_grpc, 

29 galleries_pb2_grpc, 

30 gis_pb2_grpc, 

31 groups_pb2_grpc, 

32 iris_pb2_grpc, 

33 jail_pb2_grpc, 

34 media_pb2_grpc, 

35 moderation_pb2_grpc, 

36 notifications_pb2_grpc, 

37 pages_pb2_grpc, 

38 postal_verification_pb2_grpc, 

39 public_pb2_grpc, 

40 public_trips_pb2_grpc, 

41 references_pb2_grpc, 

42 reporting_pb2_grpc, 

43 requests_pb2_grpc, 

44 resources_pb2_grpc, 

45 search_pb2_grpc, 

46 stripe_pb2_grpc, 

47 threads_pb2_grpc, 

48) 

49from couchers.servicers.account import Account, Iris 

50from couchers.servicers.admin import Admin 

51from couchers.servicers.api import API 

52from couchers.servicers.auth import Auth 

53from couchers.servicers.blocking import Blocking 

54from couchers.servicers.bugs import Bugs 

55from couchers.servicers.communities import Communities 

56from couchers.servicers.conversations import Conversations 

57from couchers.servicers.dashboard import Dashboard 

58from couchers.servicers.discussions import Discussions 

59from couchers.servicers.donations import Donations, Stripe 

60from couchers.servicers.editor import Editor 

61from couchers.servicers.events import Events 

62from couchers.servicers.galleries import Galleries 

63from couchers.servicers.gis import GIS 

64from couchers.servicers.groups import Groups 

65from couchers.servicers.jail import Jail 

66from couchers.servicers.media import Media, get_media_auth_interceptor 

67from couchers.servicers.moderation import Moderation 

68from couchers.servicers.notifications import Notifications 

69from couchers.servicers.pages import Pages 

70from couchers.servicers.postal_verification import PostalVerification 

71from couchers.servicers.public import Public 

72from couchers.servicers.public_trips import PublicTrips 

73from couchers.servicers.references import References 

74from couchers.servicers.reporting import Reporting 

75from couchers.servicers.requests import Requests 

76from couchers.servicers.resources import Resources 

77from couchers.servicers.search import Search 

78from couchers.servicers.threads import Threads 

79 

80 

81class _InstrumentedThreadPoolExecutor(futures.ThreadPoolExecutor): 

82 # gRPC submits one task per RPC 

83 def submit(self, fn: Any, /, *args: Any, **kwargs: Any) -> futures.Future[Any]: 

84 grpc_in_flight_gauge.inc() 

85 future = super().submit(fn, *args, **kwargs) 

86 future.add_done_callback(lambda _: grpc_in_flight_gauge.dec()) 

87 return future 

88 

89 

90def create_main_server(port: int, start_resource_sampler: bool = False) -> grpc.Server: 

91 executor = _InstrumentedThreadPoolExecutor(SERVER_THREADS) 

92 server = grpc.server( 

93 executor, 

94 interceptors=[ 

95 ErrorSanitizationInterceptor(), 

96 OTelInterceptor(), 

97 CouchersMiddlewareInterceptor(), 

98 ], 

99 ) 

100 if start_resource_sampler: 100 ↛ 101line 100 didn't jump to line 101 because the condition on line 100 was never true

101 start_worker_resource_sampler(executor, _get_base_engine()) 

102 server.add_insecure_port(f"[::]:{port}") 

103 

104 account_pb2_grpc.add_AccountServicer_to_server(Account(), server) 

105 admin_pb2_grpc.add_AdminServicer_to_server(Admin(), server) 

106 api_pb2_grpc.add_APIServicer_to_server(API(), server) 

107 moderation_pb2_grpc.add_ModerationServicer_to_server(Moderation(), server) 

108 auth_pb2_grpc.add_AuthServicer_to_server(Auth(), server) 

109 blocking_pb2_grpc.add_BlockingServicer_to_server(Blocking(), server) 

110 bugs_pb2_grpc.add_BugsServicer_to_server(Bugs(), server) 

111 communities_pb2_grpc.add_CommunitiesServicer_to_server(Communities(), server) 

112 conversations_pb2_grpc.add_ConversationsServicer_to_server(Conversations(), server) 

113 dashboard_pb2_grpc.add_DashboardServicer_to_server(Dashboard(), server) 

114 discussions_pb2_grpc.add_DiscussionsServicer_to_server(Discussions(), server) 

115 donations_pb2_grpc.add_DonationsServicer_to_server(Donations(), server) 

116 editor_pb2_grpc.add_EditorServicer_to_server(Editor(), server) 

117 events_pb2_grpc.add_EventsServicer_to_server(Events(), server) 

118 galleries_pb2_grpc.add_GalleriesServicer_to_server(Galleries(), server) 

119 gis_pb2_grpc.add_GISServicer_to_server(GIS(), server) 

120 groups_pb2_grpc.add_GroupsServicer_to_server(Groups(), server) 

121 iris_pb2_grpc.add_IrisServicer_to_server(Iris(), server) 

122 jail_pb2_grpc.add_JailServicer_to_server(Jail(), server) 

123 notifications_pb2_grpc.add_NotificationsServicer_to_server(Notifications(), server) 

124 pages_pb2_grpc.add_PagesServicer_to_server(Pages(), server) 

125 postal_verification_pb2_grpc.add_PostalVerificationServicer_to_server(PostalVerification(), server) 

126 public_pb2_grpc.add_PublicServicer_to_server(Public(), server) 

127 public_trips_pb2_grpc.add_PublicTripsServicer_to_server(PublicTrips(), server) 

128 references_pb2_grpc.add_ReferencesServicer_to_server(References(), server) 

129 reporting_pb2_grpc.add_ReportingServicer_to_server(Reporting(), server) 

130 requests_pb2_grpc.add_RequestsServicer_to_server(Requests(), server) 

131 resources_pb2_grpc.add_ResourcesServicer_to_server(Resources(), server) 

132 search_pb2_grpc.add_SearchServicer_to_server(Search(), server) 

133 stripe_pb2_grpc.add_StripeServicer_to_server(Stripe(), server) 

134 threads_pb2_grpc.add_ThreadsServicer_to_server(Threads(), server) 

135 return server 

136 

137 

138def create_media_server(port: int, threads: int = 8) -> grpc.Server: 

139 media_server = grpc.server( 

140 futures.ThreadPoolExecutor(threads), 

141 interceptors=[ 

142 get_media_auth_interceptor(config.MEDIA_SERVER_BEARER_TOKEN), 

143 ], 

144 ) 

145 media_server.add_insecure_port(f"[::]:{port}") 

146 media_pb2_grpc.add_MediaServicer_to_server(Media(), media_server) 

147 return media_server