Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FSSDK-10642] Refactor batch event processor #960

Merged
merged 45 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
83a7b1b
inital commit: queueing event processor
raju-opti Oct 1, 2024
9a139e4
saving
raju-opti Oct 4, 2024
be3cbd3
s
raju-opti Oct 7, 2024
e1f41ce
saving
raju-opti Oct 7, 2024
9c82bee
save
raju-opti Oct 7, 2024
4a28d26
saving
raju-opti Oct 8, 2024
5fcafc1
saving
raju-opti Oct 8, 2024
6769148
update
raju-opti Oct 9, 2024
7f13bec
update
raju-opti Oct 14, 2024
6bed2a1
update
raju-opti Oct 14, 2024
66b217f
upd
raju-opti Nov 7, 2024
bc7fcd7
test
raju-opti Nov 8, 2024
3292886
tests
raju-opti Nov 8, 2024
78908c6
tests
raju-opti Nov 8, 2024
6754a1d
up
raju-opti Nov 8, 2024
5bf1eb8
update
raju-opti Nov 8, 2024
51dd143
up
raju-opti Nov 8, 2024
b1cf28f
up
raju-opti Nov 8, 2024
1171325
u
raju-opti Nov 12, 2024
4eb8910
queing processor: wip updates
raju-opti Nov 13, 2024
4cebabc
update
raju-opti Nov 14, 2024
a2b7fe8
up
raju-opti Nov 14, 2024
37aba34
update
raju-opti Nov 14, 2024
48c9e1d
upd
raju-opti Nov 14, 2024
ee194ae
retry runner tests
raju-opti Nov 14, 2024
227c7b0
rem
raju-opti Nov 14, 2024
20771dc
more test
raju-opti Nov 15, 2024
e92f027
test
raju-opti Nov 15, 2024
e1df4a3
more test
raju-opti Nov 15, 2024
dbd3b59
bak
raju-opti Nov 15, 2024
6a64167
more tests
raju-opti Nov 18, 2024
2c411ca
new test
raju-opti Nov 18, 2024
6032ba0
factory test
raju-opti Nov 19, 2024
5b04476
cleanup
raju-opti Nov 19, 2024
8727c9b
factory test
raju-opti Nov 19, 2024
851da33
test
raju-opti Nov 20, 2024
7bb5c64
up
raju-opti Nov 20, 2024
8d73fad
react naive
raju-opti Nov 20, 2024
59b678b
node test
raju-opti Nov 20, 2024
9d684fe
more test
raju-opti Nov 20, 2024
96400ee
up
raju-opti Nov 20, 2024
d40119a
review
raju-opti Nov 21, 2024
5daa4e0
up
raju-opti Nov 21, 2024
372a1bf
up
raju-opti Nov 21, 2024
a51ff8d
up
raju-opti Nov 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/core/event_builder/build_event_v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
EventTags,
ConversionEvent,
ImpressionEvent,
} from '../../event_processor';
} from '../../event_processor/events';

import { Event } from '../../shared_types';

Expand Down
2 changes: 1 addition & 1 deletion lib/core/event_builder/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/
import { LoggerFacade } from '../../modules/logging';
import { EventV1 as CommonEventParams } from '../../event_processor';
import { EventV1 as CommonEventParams } from '../../event_processor/v1/buildEventV1';

import fns from '../../utils/fns';
import { CONTROL_ATTRIBUTES, RESERVED_EVENT_KEYWORDS } from '../../utils/enums';
Expand Down
171 changes: 171 additions & 0 deletions lib/event_processor/batch_event_processor.react_native.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/**
* Copyright 2024, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { vi, describe, it, expect, beforeEach } from 'vitest';

const mockNetInfo = vi.hoisted(() => {
const netInfo = {
listeners: [],
unsubs: [],
addEventListener(fn: any) {
this.listeners.push(fn);
const unsub = vi.fn();
this.unsubs.push(unsub);
return unsub;
},
pushState(state: boolean) {
for (const listener of this.listeners) {
listener({ isInternetReachable: state });
}
},
clear() {
this.listeners = [];
this.unsubs = [];
}
};
return netInfo;
});

vi.mock('../utils/import.react_native/@react-native-community/netinfo', () => {
return {
addEventListener: mockNetInfo.addEventListener.bind(mockNetInfo),
};
});

import { ReactNativeNetInfoEventProcessor } from './batch_event_processor.react_native';
import { getMockLogger } from '../tests/mock/mock_logger';
import { getMockRepeater } from '../tests/mock/mock_repeater';
import { getMockAsyncCache } from '../tests/mock/mock_cache';

import { EventWithId } from './batch_event_processor';
import { EventDispatcher } from './eventDispatcher';
import { formatEvents } from './v1/buildEventV1';
import { createImpressionEvent } from '../tests/mock/create_event';
import { ProcessableEvent } from './eventProcessor';

const getMockDispatcher = () => {
return {
dispatchEvent: vi.fn(),
};
};

const exhaustMicrotasks = async (loop = 100) => {
for(let i = 0; i < loop; i++) {
await Promise.resolve();
}
}


describe('ReactNativeNetInfoEventProcessor', () => {
beforeEach(() => {
mockNetInfo.clear();
});

it('should not retry failed events when reachable state does not change', async () => {
const eventDispatcher = getMockDispatcher();
const dispatchRepeater = getMockRepeater();
const failedEventRepeater = getMockRepeater();

const cache = getMockAsyncCache<EventWithId>();
const events: ProcessableEvent[] = [];

for(let i = 0; i < 5; i++) {
const id = `id-${i}`;
const event = createImpressionEvent(id);
events.push(event);
await cache.set(id, { id, event });
}

const processor = new ReactNativeNetInfoEventProcessor({
eventDispatcher,
dispatchRepeater,
failedEventRepeater,
batchSize: 1000,
eventStore: cache,
});

processor.start();
await processor.onRunning();

mockNetInfo.pushState(true);
expect(eventDispatcher.dispatchEvent).not.toHaveBeenCalled();

mockNetInfo.pushState(true);
expect(eventDispatcher.dispatchEvent).not.toHaveBeenCalled();
});

it('should retry failed events when network becomes reachable', async () => {
const eventDispatcher = getMockDispatcher();
const dispatchRepeater = getMockRepeater();
const failedEventRepeater = getMockRepeater();

const cache = getMockAsyncCache<EventWithId>();
const events: ProcessableEvent[] = [];

for(let i = 0; i < 5; i++) {
const id = `id-${i}`;
const event = createImpressionEvent(id);
events.push(event);
await cache.set(id, { id, event });
}

const processor = new ReactNativeNetInfoEventProcessor({
eventDispatcher,
dispatchRepeater,
failedEventRepeater,
batchSize: 1000,
eventStore: cache,
});

processor.start();
await processor.onRunning();

mockNetInfo.pushState(false);
expect(eventDispatcher.dispatchEvent).not.toHaveBeenCalled();

mockNetInfo.pushState(true);

await exhaustMicrotasks();

expect(eventDispatcher.dispatchEvent).toHaveBeenCalledWith(formatEvents(events));
});

it('should unsubscribe from netinfo listener when stopped', async () => {
const eventDispatcher = getMockDispatcher();
const dispatchRepeater = getMockRepeater();
const failedEventRepeater = getMockRepeater();

const cache = getMockAsyncCache<EventWithId>();

const processor = new ReactNativeNetInfoEventProcessor({
eventDispatcher,
dispatchRepeater,
failedEventRepeater,
batchSize: 1000,
eventStore: cache,
});

processor.start();
await processor.onRunning();

mockNetInfo.pushState(false);

processor.stop();
await processor.onTerminated();

expect(mockNetInfo.unsubs[0]).toHaveBeenCalled();
});
});
55 changes: 55 additions & 0 deletions lib/event_processor/batch_event_processor.react_native.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Copyright 2024, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { NetInfoState, addEventListener } from '../utils/import.react_native/@react-native-community/netinfo';

import { BatchEventProcessor, BatchEventProcessorConfig } from './batch_event_processor';
import { Fn } from '../utils/type';

export class ReactNativeNetInfoEventProcessor extends BatchEventProcessor {
private isInternetReachable = true;
private unsubscribeNetInfo?: Fn;

constructor(config: BatchEventProcessorConfig) {
super(config);
}

private async connectionListener(state: NetInfoState) {
if (this.isInternetReachable && !state.isInternetReachable) {
this.isInternetReachable = false;
return;
}

if (!this.isInternetReachable && state.isInternetReachable) {
this.isInternetReachable = true;
this.retryFailedEvents();
}
}

start(): void {
super.start();
if (addEventListener) {
this.unsubscribeNetInfo = addEventListener(this.connectionListener.bind(this));
}
}

stop(): void {
if (this.unsubscribeNetInfo) {
this.unsubscribeNetInfo();
}
super.stop();
}
}
Loading
Loading