Skip to content

Commit b4933a9

Browse files
authored
feat: allow passing http timeout to rust engine for polling (#825)
* feat: allow passing http timeout to rust engine for polling Signed-off-by: Mark Phelps <[email protected]> * chore: add to Go readme Signed-off-by: Mark Phelps <[email protected]> * chore: only install wasm-opt/pack if not installed in action Signed-off-by: Mark Phelps <[email protected]> * chore: try to fix wasm-pack command Signed-off-by: Mark Phelps <[email protected]> * chore: set version in gradle wrapper Signed-off-by: Mark Phelps <[email protected]> * chore: force install of latest wasm tools Signed-off-by: Mark Phelps <[email protected]> * chore: disable gradle cache for now Signed-off-by: Mark Phelps <[email protected]> * chore: idk what changed with wasm-pack Signed-off-by: Mark Phelps <[email protected]> --------- Signed-off-by: Mark Phelps <[email protected]>
1 parent 6452a25 commit b4933a9

File tree

11 files changed

+77
-26
lines changed

11 files changed

+77
-26
lines changed

.github/workflows/package-wasm-engine.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
run: cargo build --release --package flipt-engine-wasm --target wasm32-wasip1
4040

4141
- name: Install wasm-opt
42-
run: cargo install wasm-opt
42+
run: cargo install -f wasm-opt
4343

4444
- name: Wasm-opt
4545
run: wasm-opt --converge --flatten --rereloop -Oz -Oz --gufa -o target/wasm32-wasip1/release/flipt_engine_wasm.wasm target/wasm32-wasip1/release/flipt_engine_wasm.wasm

.github/workflows/package-wasm-js-engine.yml

+9-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
matrix:
2121
target:
2222
- web
23-
- node
23+
- nodejs
2424

2525
runs-on: ubuntu-latest
2626
env:
@@ -46,10 +46,16 @@ jobs:
4646
run: cargo build --release --package flipt-engine-wasm-js
4747

4848
- name: Install wasm-pack
49-
run: cargo install wasm-pack wasm-opt
49+
run: cargo install -f wasm-pack
50+
51+
- name: Install wasm-opt
52+
run: cargo install -f wasm-opt
5053

5154
- name: Wasm-pack
52-
run: wasm-pack build --target ${matrix.target}
55+
run: |
56+
pushd flipt-engine-wasm-js
57+
wasm-pack build --target ${{ matrix.target }}
58+
popd
5359
5460
- name: Package
5561
run: |

.github/workflows/test-android-sdk.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ jobs:
4545
- name: Setup Gradle
4646
uses: gradle/actions/setup-gradle@v4
4747
with:
48-
gradle-version: "8.11"
48+
gradle-version: "8.11.1"
49+
cache-disabled: true
4950

5051
- name: Checkout Sources
5152
uses: actions/checkout@v4

flipt-client-go/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ The `NewClient` constructor accepts a variadic number of `ClientOption` function
100100

101101
- `WithNamespace`: The namespace to fetch flag state from. If not provided, the client will default to the `default` namespace.
102102
- `WithURL`: The URL of the upstream Flipt instance. If not provided, the client will default to `http://localhost:8080`.
103+
- `WithRequestTimeout`: The timeout (in seconds) for total request time to the upstream Flipt instance. If not provided, the client will default to no timeout. Note: this only affects polling mode. Streaming mode will have no timeout set.
103104
- `WithUpdateInterval`: The interval (in seconds) in which to fetch new flag state. If not provided, the client will default to 120 seconds.
104105
- `With{Method}Authentication`: The authentication strategy to use when communicating with the upstream Flipt instance. If not provided, the client will default to no authentication. See the [Authentication](#authentication) section for more information.
105106
- `WithReference`: The [reference](https://docs.flipt.io/guides/user/using-references) to use when fetching flag state. If not provided, reference will not be used.
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#Thu Oct 31 14:43:30 WIB 2024
22
distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
4-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
4+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
5+
networkTimeout=10000
56
zipStoreBase=GRADLE_USER_HOME
67
zipStorePath=wrapper/dists

flipt-engine-ffi/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "flipt-engine-ffi"
3-
version = "0.5.0"
3+
version = "0.6.0"
44
edition = "2021"
55
authors = ["Flipt Devs <[email protected]>"]
66
license = "MIT"

flipt-engine-ffi/examples/evaluation.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ fn main() {
1111
let namespace = "default";
1212
let fetcher = HTTPFetcherBuilder::new("http://localhost:8080", namespace)
1313
.authentication(Authentication::with_client_token("secret".into()))
14-
.build();
14+
.build()
15+
.unwrap();
16+
1517
let evaluator = Evaluator::new(namespace);
1618

1719
let engine = fliptengine::Engine::new(namespace, fetcher, evaluator, ErrorStrategy::Fail);

flipt-engine-ffi/src/http.rs

+47-16
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ pub struct HTTPFetcherBuilder {
106106
namespace: String,
107107
authentication: HeaderMap,
108108
reference: Option<String>,
109+
request_timeout: Option<Duration>,
109110
update_interval: Duration,
110111
mode: FetchMode,
111112
}
@@ -120,13 +121,16 @@ struct StreamResult {
120121
namespaces: std::collections::HashMap<String, source::Document>,
121122
}
122123

124+
static USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),);
125+
123126
impl HTTPFetcherBuilder {
124127
pub fn new(base_url: &str, namespace: &str) -> Self {
125128
Self {
126129
base_url: base_url.to_string(),
127130
namespace: namespace.to_string(),
128131
authentication: HeaderMap::new(),
129132
reference: None,
133+
request_timeout: None,
130134
update_interval: Duration::from_secs(120),
131135
mode: FetchMode::default(),
132136
}
@@ -142,6 +146,11 @@ impl HTTPFetcherBuilder {
142146
self
143147
}
144148

149+
pub fn request_timeout(mut self, request_timeout: Duration) -> Self {
150+
self.request_timeout = Some(request_timeout);
151+
self
152+
}
153+
145154
pub fn update_interval(mut self, update_interval: Duration) -> Self {
146155
self.update_interval = update_interval;
147156
self
@@ -152,24 +161,37 @@ impl HTTPFetcherBuilder {
152161
self
153162
}
154163

155-
pub fn build(self) -> HTTPFetcher {
164+
pub fn build(self) -> Result<HTTPFetcher, Error> {
156165
let retry_policy = ExponentialBackoff::builder()
157166
.retry_bounds(Duration::from_secs(1), Duration::from_secs(30))
158167
.jitter(Jitter::Full)
159168
.build_with_max_retries(3);
160169

161-
HTTPFetcher {
170+
let mut client_builder = reqwest::Client::builder().user_agent(USER_AGENT);
171+
172+
if let Some(request_timeout) = self.request_timeout {
173+
// only set timeout for polling mode
174+
if request_timeout.as_nanos() > 0 && matches!(self.mode, FetchMode::Polling) {
175+
client_builder = client_builder.timeout(request_timeout);
176+
}
177+
}
178+
179+
let client = client_builder
180+
.build()
181+
.map_err(|e| Error::Internal(format!("failed to create client: {}", e)))?;
182+
183+
Ok(HTTPFetcher {
162184
base_url: self.base_url,
163185
namespace: self.namespace,
164-
http_client: ClientBuilder::new(reqwest::Client::new())
186+
http_client: ClientBuilder::new(client)
165187
.with(RetryTransientMiddleware::new_with_policy(retry_policy))
166188
.build(),
167189
authentication: self.authentication,
168190
etag: None,
169191
reference: self.reference,
170192
update_interval: self.update_interval,
171193
mode: self.mode,
172-
}
194+
})
173195
}
174196
}
175197

@@ -335,7 +357,7 @@ impl HTTPFetcher {
335357
sender
336358
.send(result)
337359
.await
338-
.map_err(|_| Error::Server("failed to send result".into()))
360+
.map_err(|_| Error::Internal("failed to send result".into()))
339361
}
340362

341363
async fn handle_streaming(
@@ -380,7 +402,7 @@ impl HTTPFetcher {
380402
match sender.send(value).await {
381403
Ok(_) => continue,
382404
Err(e) => {
383-
return Err(Error::Server(format!(
405+
return Err(Error::Internal(format!(
384406
"failed to send result to engine {}",
385407
e
386408
)))
@@ -394,7 +416,7 @@ impl HTTPFetcher {
394416
Err(e) => sender
395417
.send(Err(e.clone()))
396418
.await
397-
.map_err(|_| Error::Server("failed to send error".into())),
419+
.map_err(|_| Error::Internal("failed to send error".into())),
398420
}
399421
}
400422
}
@@ -424,7 +446,8 @@ mod tests {
424446
let url = server.url();
425447
let mut fetcher = HTTPFetcherBuilder::new(&url, "default")
426448
.authentication(Authentication::None)
427-
.build();
449+
.build()
450+
.unwrap();
428451

429452
let result = fetcher.fetch().await;
430453

@@ -452,7 +475,8 @@ mod tests {
452475
let url = server.url();
453476
let mut fetcher = HTTPFetcherBuilder::new(&url, "default")
454477
.reference("123")
455-
.build();
478+
.build()
479+
.unwrap();
456480

457481
let result = fetcher.fetch().await;
458482

@@ -475,7 +499,8 @@ mod tests {
475499
let url = server.url();
476500
let mut fetcher = HTTPFetcherBuilder::new(&url, "default")
477501
.authentication(Authentication::None)
478-
.build();
502+
.build()
503+
.unwrap();
479504

480505
fetcher.etag = Some("etag".to_string());
481506

@@ -500,7 +525,8 @@ mod tests {
500525
let url = server.url();
501526
let mut fetcher = HTTPFetcherBuilder::new(&url, "default")
502527
.authentication(Authentication::None)
503-
.build();
528+
.build()
529+
.unwrap();
504530

505531
let result = fetcher.fetch().await;
506532

@@ -524,7 +550,8 @@ mod tests {
524550
let url = server.url();
525551
let mut fetcher = HTTPFetcherBuilder::new(&url, "default")
526552
.authentication(Authentication::ClientToken("foo".to_string()))
527-
.build();
553+
.build()
554+
.unwrap();
528555

529556
let result = fetcher.fetch().await;
530557

@@ -547,7 +574,8 @@ mod tests {
547574
let url = server.url();
548575
let mut fetcher = HTTPFetcherBuilder::new(&url, "default")
549576
.authentication(Authentication::JwtToken("foo".to_string()))
550-
.build();
577+
.build()
578+
.unwrap();
551579

552580
let result = fetcher.fetch().await;
553581

@@ -570,7 +598,8 @@ mod tests {
570598
let url = server.url();
571599
let mut fetcher = HTTPFetcherBuilder::new(&url, "default")
572600
.authentication(Authentication::JwtToken("foo".to_string()))
573-
.build();
601+
.build()
602+
.unwrap();
574603

575604
let result = fetcher.fetch().await;
576605

@@ -603,7 +632,8 @@ mod tests {
603632
let mut fetcher = HTTPFetcherBuilder::new(&url, "default")
604633
.authentication(Authentication::None)
605634
.mode(FetchMode::Streaming)
606-
.build();
635+
.build()
636+
.unwrap();
607637

608638
let (tx, mut rx) = mpsc::channel(4);
609639
let result = fetcher.handle_streaming(&tx).await;
@@ -642,7 +672,8 @@ mod tests {
642672
let url = server.url();
643673
let mut fetcher = HTTPFetcherBuilder::new(&url, "default")
644674
.authentication(Authentication::None)
645-
.build();
675+
.build()
676+
.unwrap();
646677

647678
let result = fetcher.initial_fetch().await;
648679

flipt-engine-ffi/src/lib.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ fn result_to_json_ptr<T: Serialize, E: std::error::Error>(result: Result<T, E>)
8686
pub struct EngineOpts {
8787
url: Option<String>,
8888
authentication: Option<Authentication>,
89+
request_timeout: Option<u64>,
8990
update_interval: Option<u64>,
9091
fetch_mode: Option<FetchMode>,
9192
reference: Option<String>,
@@ -97,6 +98,7 @@ impl Default for EngineOpts {
9798
Self {
9899
url: Some("http://localhost:8080".into()),
99100
authentication: None,
101+
request_timeout: None,
100102
update_interval: Some(120),
101103
reference: None,
102104
fetch_mode: Some(FetchMode::default()),
@@ -407,6 +409,10 @@ unsafe extern "C" fn _initialize_engine(
407409
namespace,
408410
);
409411

412+
if let Some(request_timeout) = engine_opts.request_timeout {
413+
fetcher_builder = fetcher_builder.request_timeout(Duration::from_secs(request_timeout));
414+
}
415+
410416
if let Some(update_interval) = engine_opts.update_interval {
411417
fetcher_builder = fetcher_builder.update_interval(Duration::from_secs(update_interval));
412418
}
@@ -423,7 +429,8 @@ unsafe extern "C" fn _initialize_engine(
423429
fetcher_builder = fetcher_builder.reference(reference);
424430
}
425431

426-
let fetcher = fetcher_builder.build();
432+
let fetcher = fetcher_builder.build().unwrap();
433+
427434
let evaluator = Evaluator::new(namespace);
428435

429436
let engine = Engine::new(

flipt-evaluation/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "flipt-evaluation"
3-
version = "0.1.7"
3+
version = "0.2.0"
44
edition = "2021"
55
authors = ["Flipt Devs <[email protected]>"]
66
license = "MIT"

flipt-evaluation/src/error.rs

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ pub enum Error {
99
InvalidRequest(String),
1010
#[error("server error: {0}")]
1111
Server(String),
12+
#[error("internal error: {0}")]
13+
Internal(String),
1214
#[error("unknown error: {0}")]
1315
Unknown(String),
1416
}

0 commit comments

Comments
 (0)