Skip to content

Commit 8489c2f

Browse files
authored
fix!: reconnect, missing and duped events, remove max reconnect (#660)
Signed-off-by: Todd Baert <[email protected]>
1 parent ab754f5 commit 8489c2f

21 files changed

+207
-259
lines changed

.github/workflows/ci.yml

+10-2
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,21 @@ jobs:
3838

3939
services:
4040
flagd:
41-
image: ghcr.io/open-feature/flagd-testbed:v0.4.4
41+
image: ghcr.io/open-feature/flagd-testbed:v0.4.6
4242
ports:
4343
- 8013:8013
44+
flagd-unstable:
45+
image: ghcr.io/open-feature/flagd-testbed-unstable:v0.4.6
46+
ports:
47+
- 8014:8013
4448
sync:
45-
image: ghcr.io/open-feature/sync-testbed:v0.4.4
49+
image: ghcr.io/open-feature/sync-testbed:v0.4.6
4650
ports:
4751
- 9090:9090
52+
sync-unstable:
53+
image: ghcr.io/open-feature/sync-testbed-unstable:v0.4.6
54+
ports:
55+
- 9091:9090
4856

4957
steps:
5058
- uses: actions/checkout@v4

libs/providers/flagd/README.md

+12-13
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,16 @@ Options can be defined in the constructor or as environment variables. Construct
2828

2929
### Available Configuration Options
3030

31-
| Option name | Environment variable name | Type | Default | Supported values |
32-
|-----------------------|--------------------------------|---------|-----------|------------------|
33-
| host | FLAGD_HOST | string | localhost | |
34-
| port | FLAGD_PORT | number | 8013 | |
35-
| tls | FLAGD_TLS | boolean | false | |
36-
| socketPath | FLAGD_SOCKET_PATH | string | - | |
37-
| resolverType | FLAGD_SOURCE_RESOLVER | string | rpc | rpc, in-process |
38-
| selector | FLAGD_SOURCE_SELECTOR | string | - | |
39-
| cache | FLAGD_CACHE | string | lru | lru,disabled |
40-
| maxCacheSize | FLAGD_MAX_CACHE_SIZE | int | 1000 | |
41-
| maxEventStreamRetries | FLAGD_MAX_EVENT_STREAM_RETRIES | int | 5 | |
31+
| Option name | Environment variable name | Type | Default | Supported values |
32+
| -------------------------------------- | ------------------------------ | ------- | --------- | ---------------- |
33+
| host | FLAGD_HOST | string | localhost | |
34+
| port | FLAGD_PORT | number | 8013 | |
35+
| tls | FLAGD_TLS | boolean | false | |
36+
| socketPath | FLAGD_SOCKET_PATH | string | - | |
37+
| resolverType | FLAGD_SOURCE_RESOLVER | string | rpc | rpc, in-process |
38+
| selector | FLAGD_SOURCE_SELECTOR | string | - | |
39+
| cache | FLAGD_CACHE | string | lru | lru,disabled |
40+
| maxCacheSize | FLAGD_MAX_CACHE_SIZE | int | 1000 | |
4241

4342
Below are examples of usage patterns.
4443

@@ -80,7 +79,7 @@ In the above example, the provider expects a flag sync service implementation to
8079
The flagd provider emits `PROVIDER_READY`, `PROVIDER_ERROR` and `PROVIDER_CONFIGURATION_CHANGED` events.
8180

8281
| SDK event | Originating action in flagd |
83-
|----------------------------------|---------------------------------------------------------------------------------|
82+
| -------------------------------- | ------------------------------------------------------------------------------- |
8483
| `PROVIDER_READY` | The streaming connection with flagd has been established. |
8584
| `PROVIDER_ERROR` | The streaming connection with flagd has been broken. |
8685
| `PROVIDER_CONFIGURATION_CHANGED` | A flag configuration (default value, targeting rule, etc) in flagd has changed. |
@@ -90,7 +89,7 @@ For general information on events, see the [official documentation](https://open
9089
### Flag Metadata
9190

9291
| Field | Type | Value |
93-
|---------|--------|---------------------------------------------------|
92+
| ------- | ------ | ------------------------------------------------- |
9493
| `scope` | string | "selector" set for the associated source in flagd |
9594

9695
## Building

libs/providers/flagd/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"@openfeature/flagd-core": ">=0.1.1"
1010
},
1111
"peerDependencies": {
12-
"@grpc/grpc-js": "^1.6.0",
12+
"@grpc/grpc-js": "~1.8.0",
1313
"@openfeature/server-sdk": ">=1.6.0"
1414
}
1515
}

libs/providers/flagd/src/e2e/jest.config.ts

+1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ export default {
77
moduleNameMapper: {
88
'@openfeature/flagd-core': ['<rootDir>/../../../../shared/flagd-core/src'],
99
},
10+
globalTeardown: './tear-down.ts',
1011
};

libs/providers/flagd/src/e2e/setup-in-process-provider.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ const FLAGD_NAME = 'flagd Provider';
77
// register the flagd provider before the tests.
88
console.log('Setting flagd provider...');
99
OpenFeature.setProvider(
10+
'e2e',
1011
new FlagdProvider({ cache: 'disabled', resolverType: 'in-process', host: 'localhost', port: 9090 }),
1112
);
12-
assert(
13-
OpenFeature.providerMetadata.name === FLAGD_NAME,
14-
new Error(`Expected ${FLAGD_NAME} provider to be configured, instead got: ${OpenFeature.providerMetadata.name}`),
15-
);
13+
OpenFeature.setProvider('unstable', new FlagdProvider({ resolverType: 'in-process', host: 'localhost', port: 9091 }));
14+
// TODO: update with correct assertions once we have ability to get providerMetadata for any provider
15+
// assert(
16+
// OpenFeature.providerMetadata.name === FLAGD_NAME,
17+
// new Error(`Expected ${FLAGD_NAME} provider to be configured, instead got: ${OpenFeature.providerMetadata.name}`),
18+
// );
1619
console.log('flagd provider configured!');

libs/providers/flagd/src/e2e/setup-rpc-provider.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ const FLAGD_NAME = 'flagd Provider';
66

77
// register the flagd provider before the tests.
88
console.log('Setting flagd provider...');
9-
OpenFeature.setProvider(new FlagdProvider({ cache: 'disabled' }));
10-
assert(
11-
OpenFeature.providerMetadata.name === FLAGD_NAME,
12-
new Error(`Expected ${FLAGD_NAME} provider to be configured, instead got: ${OpenFeature.providerMetadata.name}`),
13-
);
9+
OpenFeature.setProvider('e2e', new FlagdProvider({ cache: 'disabled' }));
10+
OpenFeature.setProvider('unstable', new FlagdProvider({ cache: 'disabled', port: 8014 }));
11+
// TODO: update with correct assertions once we have ability to get providerMetadata for any provider
12+
// assert(
13+
// OpenFeature.providerMetadata.name === FLAGD_NAME,
14+
// new Error(`Expected ${FLAGD_NAME} provider to be configured, instead got: ${OpenFeature.providerMetadata.name}`),
15+
// );
1416
console.log('flagd provider configured!');

libs/providers/flagd/src/e2e/step-definitions/evaluation.spec.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { defineFeature, loadFeature } from 'jest-cucumber';
1414
const feature = loadFeature('features/evaluation.feature');
1515

1616
// get a client (flagd provider registered in setup)
17-
const client = OpenFeature.getClient();
17+
const client = OpenFeature.getClient('e2e');
1818

1919
const givenAnOpenfeatureClientIsRegistered = (
2020
given: (stepMatcher: string, stepDefinitionCallback: () => void) => void,
@@ -29,10 +29,6 @@ defineFeature(feature, (test) => {
2929
});
3030
});
3131

32-
afterAll(async () => {
33-
await OpenFeature.close();
34-
});
35-
3632
test('Resolves boolean value', ({ given, when, then }) => {
3733
let value: boolean;
3834
let flagKey: string;

libs/providers/flagd/src/e2e/step-definitions/flagd-json-evaluator.spec.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { StepsDefinitionCallbackFunction } from 'jest-cucumber/dist/src/feature-
66
const feature = loadFeature('features/flagd-json-evaluator.feature');
77

88
// get a client (flagd provider registered in setup)
9-
const client = OpenFeature.getClient();
9+
const client = OpenFeature.getClient('e2e');
1010

1111
const aFlagProviderIsSet = (given: (stepMatcher: string, stepDefinitionCallback: () => void) => void) => {
1212
given('a flagd provider is set', () => undefined);
@@ -39,10 +39,6 @@ defineFeature(feature, (test) => {
3939
});
4040
});
4141

42-
afterAll(async () => {
43-
await OpenFeature.close();
44-
});
45-
4642
test('Evaluator reuse', evaluateStringFlagWithContext);
4743

4844
test('Fractional operator', ({ given, when, and, then }) => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { OpenFeature, ProviderEvents } from '@openfeature/server-sdk';
2+
import { defineFeature, loadFeature } from 'jest-cucumber';
3+
4+
jest.setTimeout(30000);
5+
6+
// load the feature file.
7+
const feature = loadFeature('features/flagd-reconnect.feature');
8+
9+
// get a client (flagd provider registered in setup)
10+
const client = OpenFeature.getClient('unstable');
11+
12+
defineFeature(feature, (test) => {
13+
let readyRunCount = 0;
14+
let errorRunCount = 0;
15+
16+
beforeAll((done) => {
17+
client.addHandler(ProviderEvents.Ready, async () => {
18+
readyRunCount++;
19+
done();
20+
});
21+
});
22+
23+
test('Provider reconnection', ({ given, when, then, and }) => {
24+
given('a flagd provider is set', () => {
25+
// handled in beforeAll
26+
});
27+
when('a PROVIDER_READY handler and a PROVIDER_ERROR handler are added', () => {
28+
client.addHandler(ProviderEvents.Error, () => {
29+
errorRunCount++;
30+
});
31+
});
32+
then('the PROVIDER_READY handler must run when the provider connects', async () => {
33+
// should already be at 1 from `beforeAll`
34+
expect(readyRunCount).toEqual(1);
35+
});
36+
and("the PROVIDER_ERROR handler must run when the provider's connection is lost", async () => {
37+
await new Promise((resolve) => setTimeout(resolve, 10000));
38+
expect(errorRunCount).toBeGreaterThan(0);
39+
});
40+
and('when the connection is reestablished the PROVIDER_READY handler must run again', async () => {
41+
await new Promise((resolve) => setTimeout(resolve, 10000));
42+
expect(readyRunCount).toBeGreaterThan(1);
43+
});
44+
});
45+
});

libs/providers/flagd/src/e2e/step-definitions/flagd.spec.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { defineFeature, loadFeature } from 'jest-cucumber';
55
const feature = loadFeature('features/flagd.feature');
66

77
// get a client (flagd provider registered in setup)
8-
const client = OpenFeature.getClient();
8+
const client = OpenFeature.getClient('e2e');
99

1010
const aFlagProviderIsSet = (given: (stepMatcher: string, stepDefinitionCallback: () => void) => void) => {
1111
given('a flagd provider is set', () => undefined);
@@ -18,10 +18,6 @@ defineFeature(feature, (test) => {
1818
});
1919
});
2020

21-
afterAll(async () => {
22-
await OpenFeature.close();
23-
});
24-
2521
test('Provider ready event', ({ given, when, then }) => {
2622
let ran = false;
2723

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { OpenFeature } from '@openfeature/server-sdk';
2+
3+
const tearDownTests = async () => {
4+
console.log('Shutting down OpenFeature...');
5+
await OpenFeature.close();
6+
};
7+
8+
export default tearDownTests;

libs/providers/flagd/src/lib/configuration.spec.ts

-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ describe('Configuration', () => {
1515
port: 8013,
1616
tls: false,
1717
maxCacheSize: DEFAULT_MAX_CACHE_SIZE,
18-
maxEventStreamRetries: DEFAULT_MAX_EVENT_STREAM_RETRIES,
1918
cache: 'lru',
2019
resolverType: 'rpc',
2120
selector: '',
@@ -28,7 +27,6 @@ describe('Configuration', () => {
2827
const tls = true;
2928
const socketPath = '/tmp/flagd.socks';
3029
const maxCacheSize = 333;
31-
const maxEventStreamRetries = 10;
3230
const cache = 'disabled';
3331
const resolverType = 'in-process';
3432
const selector = 'app=weather';
@@ -39,7 +37,6 @@ describe('Configuration', () => {
3937
process.env['FLAGD_SOCKET_PATH'] = socketPath;
4038
process.env['FLAGD_CACHE'] = cache;
4139
process.env['FLAGD_MAX_CACHE_SIZE'] = `${maxCacheSize}`;
42-
process.env['FLAGD_MAX_EVENT_STREAM_RETRIES'] = `${maxEventStreamRetries}`;
4340
process.env['FLAGD_SOURCE_SELECTOR'] = `${selector}`;
4441
process.env['FLAGD_RESOLVER'] = `${resolverType}`;
4542

@@ -49,7 +46,6 @@ describe('Configuration', () => {
4946
tls,
5047
socketPath,
5148
maxCacheSize,
52-
maxEventStreamRetries,
5349
cache,
5450
resolverType,
5551
selector,
@@ -62,7 +58,6 @@ describe('Configuration', () => {
6258
port: 3000,
6359
tls: true,
6460
maxCacheSize: 1000,
65-
maxEventStreamRetries: 5,
6661
cache: 'lru',
6762
resolverType: 'rpc',
6863
selector: '',

libs/providers/flagd/src/lib/configuration.ts

-12
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,6 @@ export interface Config {
6262
* @default 1000
6363
*/
6464
maxCacheSize?: number;
65-
66-
/**
67-
* Amount of times to attempt to reconnect to the event stream.
68-
*
69-
* @default 5
70-
*/
71-
maxEventStreamRetries?: number;
7265
}
7366

7467
export type FlagdProviderOptions = Partial<Config>;
@@ -81,7 +74,6 @@ const DEFAULT_CONFIG: Config = {
8174
selector: '',
8275
cache: 'lru',
8376
maxCacheSize: DEFAULT_MAX_CACHE_SIZE,
84-
maxEventStreamRetries: DEFAULT_MAX_EVENT_STREAM_RETRIES,
8577
};
8678

8779
enum ENV_VAR {
@@ -91,7 +83,6 @@ enum ENV_VAR {
9183
FLAGD_SOCKET_PATH = 'FLAGD_SOCKET_PATH',
9284
FLAGD_CACHE = 'FLAGD_CACHE',
9385
FLAGD_MAX_CACHE_SIZE = 'FLAGD_MAX_CACHE_SIZE',
94-
FLAGD_MAX_EVENT_STREAM_RETRIES = 'FLAGD_MAX_EVENT_STREAM_RETRIES',
9586
FLAGD_SOURCE_SELECTOR = 'FLAGD_SOURCE_SELECTOR',
9687
FLAGD_RESOLVER = 'FLAGD_RESOLVER',
9788
}
@@ -115,9 +106,6 @@ const getEnvVarConfig = (): Partial<Config> => ({
115106
...(process.env[ENV_VAR.FLAGD_MAX_CACHE_SIZE] && {
116107
maxCacheSize: Number(process.env[ENV_VAR.FLAGD_MAX_CACHE_SIZE]),
117108
}),
118-
...(process.env[ENV_VAR.FLAGD_MAX_EVENT_STREAM_RETRIES] && {
119-
maxEventStreamRetries: Number(process.env[ENV_VAR.FLAGD_MAX_EVENT_STREAM_RETRIES]),
120-
}),
121109
...(process.env[ENV_VAR.FLAGD_SOURCE_SELECTOR] && {
122110
selector: process.env[ENV_VAR.FLAGD_SOURCE_SELECTOR],
123111
}),
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export const BASE_EVENT_STREAM_RETRY_BACKOFF_MS = 1000;
2-
export const DEFAULT_MAX_EVENT_STREAM_RETRIES = 5;
2+
export const DEFAULT_MAX_EVENT_STREAM_RETRIES = Infinity;
33
export const EVENT_CONFIGURATION_CHANGE = 'configuration_change';
44
export const EVENT_PROVIDER_READY = 'provider_ready';
55
export const DEFAULT_MAX_CACHE_SIZE = 1000;

0 commit comments

Comments
 (0)