@@ -2,29 +2,25 @@ use anyhow::Context;
22pub use macro_env:: Environment ;
33use macro_env_var:: { env_vars, maybe_env_vars} ;
44use macro_service_urls:: LexicalServiceUrl ;
5+ use secretsmanager_client:: LocalOrRemoteSecret ;
56
67env_vars ! {
7- struct DatabaseUrl ;
8- struct SearchEventQueue ;
9- struct OpensearchUrl ;
10- struct OpensearchUsername ;
11- struct OpensearchPassword ;
12- struct DocumentStorageBucket ;
13- struct BackfillJobsTable ;
8+ pub struct DatabaseUrl ;
9+ pub struct SearchEventQueue ;
10+ pub struct OpensearchUrl ;
11+ pub struct OpensearchUsername ;
12+ pub struct OpensearchPassword ;
13+ pub struct DocumentStorageBucket ;
14+ pub struct BackfillJobsTable ;
1415}
1516
1617maybe_env_vars ! {
17- struct DatabaseUrlReadonly ;
18- struct Port ;
19- struct QueueMaxMessages ;
20- struct QueueWaitTimeSeconds ;
21- struct WorkerCount ;
22- struct BackfillCallsPageSize ;
23- struct BackfillChatsPageSize ;
24- struct BackfillChannelsPageSize ;
25- struct BackfillDocumentsPageSize ;
26- struct BackfillEmailsPageSize ;
27- struct BackfillJobTtlSeconds ;
18+ pub struct BackfillCallsPageSize ;
19+ pub struct BackfillChatsPageSize ;
20+ pub struct BackfillChannelsPageSize ;
21+ pub struct BackfillDocumentsPageSize ;
22+ pub struct BackfillEmailsPageSize ;
23+ pub struct BackfillJobTtlSeconds ;
2824}
2925
3026/// Per-entity DB page sizes used by the backfill source adapters. Tunable at
@@ -43,11 +39,41 @@ const DEFAULT_CHATS_PAGE: usize = 5000;
4339const DEFAULT_CHANNELS_PAGE : usize = 5000 ;
4440const DEFAULT_DOCUMENTS_PAGE : usize = 1000 ;
4541const DEFAULT_EMAILS_PAGE : usize = 1000 ;
42+ const DEFAULT_BACKFILL_JOB_TTL_SECONDS : u64 = 24 * 60 * 60 ;
4643
44+ fn parse_page_size ( name : & str , raw_value : Option < & str > , default : usize ) -> anyhow:: Result < usize > {
45+ let page_size = raw_value
46+ . map ( |raw| {
47+ raw. parse :: < usize > ( )
48+ . with_context ( || format ! ( "{name} must be a positive integer" ) )
49+ } )
50+ . transpose ( ) ?
51+ . unwrap_or ( default) ;
52+
53+ if page_size == 0 {
54+ anyhow:: bail!( "{name} must be > 0" ) ;
55+ }
56+
57+ Ok ( page_size)
58+ }
59+
60+ fn parse_u64 ( name : & str , raw_value : Option < & str > , default : u64 ) -> anyhow:: Result < u64 > {
61+ raw_value
62+ . map ( |raw| {
63+ raw. parse :: < u64 > ( )
64+ . with_context ( || format ! ( "{name} must be a positive integer" ) )
65+ } )
66+ . transpose ( )
67+ . map ( |value| value. unwrap_or ( default) )
68+ }
69+
70+ /// The configuration parameters for the application.
71+ #[ derive( macro_config:: MacroConfig ) ]
72+ #[ serde( rename_all = "SCREAMING_SNAKE_CASE" ) ]
4773pub struct Config {
4874 /// The connection URL for the Postgres database this application should use.
4975 /// For deployed applications, this is a secret stored in AWS Secrets Manager.
50- pub database_url : String ,
76+ pub database_url : LocalOrRemoteSecret < DatabaseUrl > ,
5177
5278 /// Optional connection URL (or SM secret id when `environment != Local`)
5379 /// for the macrodb read-replica. When present, backfill reads run against
@@ -58,181 +84,101 @@ pub struct Config {
5884 pub database_url_readonly : Option < String > ,
5985
6086 /// The port to listen for HTTP requests on.
87+ #[ macro_config_default( 8080 ) ]
6188 pub port : usize ,
6289
6390 /// The search text extractor queue
64- pub search_event_queue : String ,
91+ pub search_event_queue : SearchEventQueue ,
6592 /// The queue max messages per poll
93+ #[ macro_config_default( 10 ) ]
6694 pub queue_max_messages : i32 ,
6795 /// The queue wait time seconds
96+ #[ macro_config_default( 20 ) ]
6897 pub queue_wait_time_seconds : i32 ,
6998
7099 /// The environment we are in
100+ #[ macro_config_default( Environment :: new_or_prod( ) ) ]
71101 pub environment : Environment ,
72102
73103 /// The URL for the Opensearch instance
74- pub opensearch_url : String ,
104+ pub opensearch_url : OpensearchUrl ,
75105 /// The username for the Opensearch instance
76- pub opensearch_username : String ,
106+ pub opensearch_username : OpensearchUsername ,
77107 /// The password for the Opensearch instance
78- pub opensearch_password : String ,
108+ pub opensearch_password : LocalOrRemoteSecret < OpensearchPassword > ,
79109
80110 /// The bucket where documents are stored
81- pub document_storage_bucket : String ,
111+ pub document_storage_bucket : DocumentStorageBucket ,
82112
83113 /// The number of workers to spawn
114+ #[ macro_config_default( 10 ) ]
84115 pub worker_count : u8 ,
85116
86117 /// The URL for the Lexical service
118+ #[ macro_config_default( LexicalServiceUrl :: unwrap_new( ) . to_string( ) ) ]
87119 pub lexical_service_url : String ,
88120
89- /// Per-entity DB page sizes for backfill adapters.
90- pub backfill_page_sizes : BackfillPageSizes ,
121+ /// DB page size used when backfilling call records.
122+ pub backfill_calls_page_size : BackfillCallsPageSize ,
123+ /// DB page size used when backfilling chats.
124+ pub backfill_chats_page_size : BackfillChatsPageSize ,
125+ /// DB page size used when backfilling channels.
126+ pub backfill_channels_page_size : BackfillChannelsPageSize ,
127+ /// DB page size used when backfilling documents.
128+ pub backfill_documents_page_size : BackfillDocumentsPageSize ,
129+ /// DB page size used when backfilling emails.
130+ pub backfill_emails_page_size : BackfillEmailsPageSize ,
91131
92132 /// DynamoDB table name backing the backfill job registry. Items carry an
93133 /// `expires_at` epoch attribute that DynamoDB's TTL sweeps in the
94134 /// background, so completed jobs vanish on their own.
95- pub backfill_jobs_table : String ,
135+ pub backfill_jobs_table : BackfillJobsTable ,
96136
97137 /// TTL applied to the `expires_at` attribute on each job record. Acts as
98138 /// the GC mechanism — DynamoDB removes items shortly after this elapses.
99- pub backfill_job_ttl_seconds : u64 ,
100- }
101-
102- fn parse_page_size ( name : & str , raw_value : Option < String > , default : usize ) -> anyhow:: Result < usize > {
103- match raw_value {
104- Some ( raw) => raw
105- . parse :: < usize > ( )
106- . with_context ( || format ! ( "{name} must be a positive integer" ) )
107- . and_then ( |n| {
108- if n == 0 {
109- anyhow:: bail!( "{name} must be > 0" ) ;
110- }
111- Ok ( n)
112- } ) ,
113- None => Ok ( default) ,
114- }
139+ pub backfill_job_ttl_seconds : BackfillJobTtlSeconds ,
115140}
116141
117142impl Config {
118143 pub fn from_env ( ) -> anyhow:: Result < Self > {
119- let database_url = DatabaseUrl :: new ( )
120- . context ( "DATABASE_URL must be provided" ) ?
121- . to_string ( ) ;
122-
123- let database_url_readonly = DatabaseUrlReadonly :: new ( ) . map ( |url| url. to_string ( ) ) ;
124-
125- let port = Port :: new ( )
126- . map ( |port| port. parse :: < usize > ( ) . context ( "should be valid port number" ) )
127- . transpose ( ) ?
128- . unwrap_or ( 8080 ) ;
129-
130- let environment = Environment :: new_or_prod ( ) ;
131-
132- let search_event_queue = SearchEventQueue :: new ( )
133- . context ( "SEARCH_EVENT_QUEUE must be provided" ) ?
134- . to_string ( ) ;
135-
136- let queue_max_messages = QueueMaxMessages :: new ( )
137- . map ( |queue_max_messages| {
138- queue_max_messages
139- . parse :: < i32 > ( )
140- . context ( "QUEUE_MAX_MESSAGES must be a valid i32" )
141- } )
142- . transpose ( ) ?
143- . unwrap_or ( 10 ) ;
144-
145- let queue_wait_time_seconds = QueueWaitTimeSeconds :: new ( )
146- . map ( |queue_wait_time_seconds| {
147- queue_wait_time_seconds
148- . parse :: < i32 > ( )
149- . context ( "QUEUE_WAIT_TIME_SECONDS must be a valid i32" )
150- } )
151- . transpose ( ) ?
152- . unwrap_or ( 20 ) ;
153-
154- let opensearch_url = OpensearchUrl :: new ( )
155- . context ( "OPENSEARCH_URL must be provided" ) ?
156- . to_string ( ) ;
157- let opensearch_username = OpensearchUsername :: new ( )
158- . context ( "OPENSEARCH_USERNAME must be provided" ) ?
159- . to_string ( ) ;
160- let opensearch_password = OpensearchPassword :: new ( )
161- . context ( "OPENSEARCH_PASSWORD must be provided" ) ?
162- . to_string ( ) ;
163-
164- let document_storage_bucket = DocumentStorageBucket :: new ( )
165- . context ( "DOCUMENT_STORAGE_BUCKET must be provided" ) ?
166- . to_string ( ) ;
167-
168- let worker_count = WorkerCount :: new ( )
169- . map ( |worker_count| {
170- worker_count
171- . parse :: < u8 > ( )
172- . context ( "WORKER_COUNT must be a valid u8" )
173- } )
174- . transpose ( ) ?
175- . unwrap_or ( 10 ) ;
176-
177- let lexical_service_url = LexicalServiceUrl :: new ( ) ?. to_string ( ) ;
178-
179- let backfill_page_sizes = BackfillPageSizes {
144+ macro_config:: ConfigLoader :: load :: < Config > ( ) . context ( "failed to load config" )
145+ }
146+
147+ pub fn backfill_page_sizes ( & self ) -> anyhow:: Result < BackfillPageSizes > {
148+ Ok ( BackfillPageSizes {
180149 calls : parse_page_size (
181150 "BACKFILL_CALLS_PAGE_SIZE" ,
182- BackfillCallsPageSize :: new ( ) . map ( |value| value . to_string ( ) ) ,
151+ self . backfill_calls_page_size . value ( ) ,
183152 DEFAULT_CALLS_PAGE ,
184153 ) ?,
185154 chats : parse_page_size (
186155 "BACKFILL_CHATS_PAGE_SIZE" ,
187- BackfillChatsPageSize :: new ( ) . map ( |value| value . to_string ( ) ) ,
156+ self . backfill_chats_page_size . value ( ) ,
188157 DEFAULT_CHATS_PAGE ,
189158 ) ?,
190159 channels : parse_page_size (
191160 "BACKFILL_CHANNELS_PAGE_SIZE" ,
192- BackfillChannelsPageSize :: new ( ) . map ( |value| value . to_string ( ) ) ,
161+ self . backfill_channels_page_size . value ( ) ,
193162 DEFAULT_CHANNELS_PAGE ,
194163 ) ?,
195164 documents : parse_page_size (
196165 "BACKFILL_DOCUMENTS_PAGE_SIZE" ,
197- BackfillDocumentsPageSize :: new ( ) . map ( |value| value . to_string ( ) ) ,
166+ self . backfill_documents_page_size . value ( ) ,
198167 DEFAULT_DOCUMENTS_PAGE ,
199168 ) ?,
200169 emails : parse_page_size (
201170 "BACKFILL_EMAILS_PAGE_SIZE" ,
202- BackfillEmailsPageSize :: new ( ) . map ( |value| value . to_string ( ) ) ,
171+ self . backfill_emails_page_size . value ( ) ,
203172 DEFAULT_EMAILS_PAGE ,
204173 ) ?,
205- } ;
206-
207- let backfill_jobs_table = BackfillJobsTable :: new ( )
208- . context ( "BACKFILL_JOBS_TABLE must be provided" ) ?
209- . to_string ( ) ;
210- let backfill_job_ttl_seconds = BackfillJobTtlSeconds :: new ( )
211- . map ( |backfill_job_ttl_seconds| {
212- backfill_job_ttl_seconds
213- . parse :: < u64 > ( )
214- . context ( "BACKFILL_JOB_TTL_SECONDS must be a positive integer" )
215- } )
216- . transpose ( ) ?
217- . unwrap_or ( 24 * 60 * 60 ) ;
218-
219- Ok ( Config {
220- database_url,
221- database_url_readonly,
222- port,
223- search_event_queue,
224- queue_max_messages,
225- queue_wait_time_seconds,
226- environment,
227- opensearch_url,
228- opensearch_username,
229- opensearch_password,
230- document_storage_bucket,
231- worker_count,
232- lexical_service_url,
233- backfill_page_sizes,
234- backfill_jobs_table,
235- backfill_job_ttl_seconds,
236174 } )
237175 }
176+
177+ pub fn backfill_job_ttl_seconds ( & self ) -> anyhow:: Result < u64 > {
178+ parse_u64 (
179+ "BACKFILL_JOB_TTL_SECONDS" ,
180+ self . backfill_job_ttl_seconds . value ( ) ,
181+ DEFAULT_BACKFILL_JOB_TTL_SECONDS ,
182+ )
183+ }
238184}
0 commit comments