Skip to content

Commit

Permalink
feat(app-load-balancing): update load-balancing logic
Browse files Browse the repository at this point in the history
  • Loading branch information
albertodigioacchino committed Jan 25, 2021
1 parent bc171f5 commit 93a32de
Show file tree
Hide file tree
Showing 14 changed files with 35 additions and 59 deletions.
5 changes: 5 additions & 0 deletions fixtures/firestore-data/rtdbInstances.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"default-rtdb": {
"onlineOnGameCount": 0
}
}
1 change: 1 addition & 0 deletions packages/game-app/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ REACT_APP_FIREBASE_CONFIG_APP_ID=
REACT_APP_FIREBASE_USE_EMULATORS=
FIREBASE_AUTH_EMULATOR_HOST=
FIRESTORE_EMULATOR_HOST=
REACT_APP_FUNCTIONS_BASE_URL=
14 changes: 0 additions & 14 deletions packages/game-app/src/_shared/auth/saga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,11 @@ function getCurrentUser(): Promise<AuthUser | null> {
});
}

async function getToken(): Promise<string | undefined> {
return firebase.auth().currentUser?.getIdToken();
}

function* initializeAuthSaga() {
const user: AuthUser | null = yield call(getCurrentUser);
const token: string | undefined = yield call(getToken);
if (user) {
user.token = token;
}
yield put(actions.setLoggedUser(user));
}

function* executeGetToken() {
const token: string | undefined = yield call(getToken);
return token;
}

function* resendVerificationEmail() {
yield call(() => firebase.auth().currentUser?.sendEmailVerification());
}
Expand Down Expand Up @@ -90,5 +77,4 @@ export default function* authSaga() {
yield takeEvery(actions.verifyEmail, addRequestStatusManagement(executeEmailVerification, 'auth.emailVerification'));
yield takeEvery(actions.login, addRequestStatusManagement(executeLogin, 'auth.login'));
yield takeEvery(actions.logout, addRequestStatusManagement(executeLogout, 'auth.logout'));
yield takeEvery(actions.getToken, executeGetToken);
}
1 change: 0 additions & 1 deletion packages/game-app/src/_shared/auth/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ export interface AuthUser {
id: string;
email: string;
emailVerified: boolean;
token?: string;
}

export interface State {
Expand Down
2 changes: 1 addition & 1 deletion packages/game-app/src/_shared/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export interface Config {
REACT_APP_FIREBASE_CONFIG_MESSAGING_SENDER_ID: string;
REACT_APP_FIREBASE_CONFIG_APP_ID: string;
REACT_APP_FIREBASE_USE_EMULATORS: string;
REACT_APP_BASE_URL: string;
REACT_APP_FUNCTIONS_BASE_URL: string;
}
2 changes: 2 additions & 0 deletions packages/game-app/src/_shared/store/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { name as authName, reducer as authReducer } from '@pipeline/auth';
import { name as requestsStatusName, reducer as requestsStatusReducer } from '@pipeline/requests-status';
import { name as dynamicDataName, reducer as dynamicDataReducer } from '@pipeline/dynamicData';
import { name as gameName, reducer as gameReducer } from '../../gameView/slice';
import { name as loadBalancerName, reducer as loadBalancerReducer } from '../../loadBalancer/slice';

const reducers = {
[i18nName]: i18nReducer,
[authName]: authReducer,
[requestsStatusName]: requestsStatusReducer,
[dynamicDataName]: dynamicDataReducer,
[gameName]: gameReducer,
[loadBalancerName]: loadBalancerReducer,
};

export default reducers;
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { PanelMode } from '../DeckPanel/DeckPanel';
import TopWidgetsRow from '../TopWidgetsRow';
import ZoomPanContainer from '../ZoomPanContainer';
import ZoomPanContext from '../ZoomPanContext';
import useStopPollingOnlineStatus from '../../hooks/useStopPollingOnlineStatus';
import useStopListenOnlineStatus from '../../hooks/useStopListenOnlineStatus';

type GameProps = {
zoomIn: () => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ import { batch, useDispatch } from 'react-redux';
import { actions as loadBalancerActions } from '../../loadBalancer/slice';
import { useEffect } from 'react';

export default function useStopPollingOnlineStatus() {
export default function useStopListenOnlineStatus() {
const dispatch = useDispatch();

useEffect(() => {
return () => {
batch(() => {
dispatch(loadBalancerActions.stopPollingOnlineStatus());
dispatch(loadBalancerActions.stopListenToOnlineStatus());
});
};
Expand Down
4 changes: 1 addition & 3 deletions packages/game-app/src/gameView/sagas/loadGame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,17 @@ import { addRequestStatusManagement } from '@pipeline/requests-status';
import { CardEntity, CardTypes, Game } from '@pipeline/common';
import loadGame from '../apis/callLoadGame';
import loadCardsForDeck from '../apis/callLoadCardsForDeck';
import { AuthUser, selectors as authSelectors } from '@pipeline/auth';

function* executeLoadGame(action: ReturnType<typeof actions.loadGame>) {
const game: Game = yield call(loadGame, action.payload);
const user: AuthUser = yield select(authSelectors.getCurrentUser);
const cards: CardEntity[] = yield call(loadCardsForDeck, game.deckId);
yield put(actions.saveCards(cards));
// TODO load actual game state from firestore

if (game.rtdbInstance) {
yield put(actions.saveGame(game));
} else {
const bestRTDBInstance = yield call(selectBestRTDBInstance, action.payload, user.token!);
const bestRTDBInstance = yield call(selectBestRTDBInstance, action.payload);
yield put(actions.saveGame({ ...game, rtdbInstance: bestRTDBInstance }));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export async function startListenToOnlineStatus(
gameId,
} as Status;

console.log('Connecting to db');
rtdb.ref('.info/connected').on('value', async snapshot => {
if (snapshot.val() === false) return;
await userStatusDatabaseRef.onDisconnect().set(isOfflineForDatabase, () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import axios from 'axios';
import CONFIG from '@pipeline/app-config';
import firebase from 'firebase';

export default async function selectBestRTDBInstance(gameId: string, token: string): Promise<string> {
const res = await axios.get(`${CONFIG.REACT_APP_BASE_URL}/selectBestRTDBInstance?gameId=${gameId}`, {
export default async function selectBestRTDBInstance(gameId: string): Promise<string> {
const token = await firebase.auth().currentUser?.getIdToken();
const res = await axios.get(`${CONFIG.REACT_APP_FUNCTIONS_BASE_URL}selectBestRTDBInstance?gameId=${gameId}`, {
headers: {
'Authorization:': `Bearer ${token}`,
authorization: `Bearer ${token}`,
},
});
return res.data.bestRTDBInstanceId;
return res.data.bestRTDBInstanceName;
}
6 changes: 2 additions & 4 deletions packages/game-app/src/loadBalancer/sagas/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import {
initializeRTDB,
pollUpdateStatusSagaWatcher,
startListenToOnlineStatusSaga,
stopListenToOnlineStatusSaga,
updateOnlineStatusSaga,
watchStatusChannel,
startPolling,
startListen,
} from './loadBalancer';
import { all } from 'redux-saga/effects';

Expand All @@ -15,8 +14,7 @@ export default function* loadBalancerSaga() {
updateOnlineStatusSaga(),
startListenToOnlineStatusSaga(),
stopListenToOnlineStatusSaga(),
pollUpdateStatusSagaWatcher(),
watchStatusChannel(),
startPolling(),
startListen(),
]);
}
39 changes: 12 additions & 27 deletions packages/game-app/src/loadBalancer/sagas/loadBalancer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { call, put, select, takeEvery, delay, take, race } from 'redux-saga/effects';
import { call, put, select, takeEvery, take } from 'redux-saga/effects';
import { channel } from 'redux-saga';
import { actions, selectors as loadBalancerSelectors } from '../slice';
import { actions as loadGameActions, selectors as gameSelectors } from '../../gameView/slice';
Expand Down Expand Up @@ -32,6 +32,7 @@ function* executeStartListenToOnlineStatus(action: ReturnType<typeof actions.sta
const rtdb: firebase.database.Database = yield select(loadBalancerSelectors.getRTDB);
const user: AuthUser = yield select(authSelectors.getCurrentUser);
const gameId: string = yield select(gameSelectors.getSelectedGameId);
console.log('executeStartListenToOnlineStatus');
yield call(
startListenToOnlineStatus,
rtdb,
Expand All @@ -41,7 +42,7 @@ function* executeStartListenToOnlineStatus(action: ReturnType<typeof actions.sta
statusChannel.put(actions.updateOnlineStatusSuccess({ state: 'online', gameId }));
},
() => {
statusChannel.put(actions.updateOnlineStatusSuccess({ state: 'offline', gameId }));
statusChannel.put(actions.updateOnlineStatusSuccess({ state: 'offline', gameId: null }));
},
);
}
Expand All @@ -59,24 +60,6 @@ export function* stopListenToOnlineStatusSaga() {
yield takeEvery(actions.stopListenToOnlineStatus, executeStopListenToOnlineStatus);
}

function* pollUpdateStatusSagaWorker() {
while (true) {
const rtdb: firebase.database.Database = yield select(loadBalancerSelectors.getRTDB);
const user: AuthUser = yield select(authSelectors.getCurrentUser);
const gameId: string = yield select(gameSelectors.getSelectedGameId);
yield call(callUpdateOnlineStatus, rtdb, user.id, gameId, 'online');
yield put(actions.updateOnlineStatusSuccess({ state: 'online', gameId }));
yield call(delay, 5000);
}
}

export function* pollUpdateStatusSagaWatcher() {
while (true) {
yield take(actions.startPollingOnlineStatus);
yield race([call(pollUpdateStatusSagaWorker), take(actions.stopPollingOnlineStatus)]);
}
}

export function* watchStatusChannel() {
while (true) {
const action = yield take(statusChannel);
Expand All @@ -85,9 +68,12 @@ export function* watchStatusChannel() {
}

function* executeInitializeRTDB(action: ReturnType<typeof loadGameActions.saveGame>) {
const app = firebase.initializeApp({
databaseURL: `https://${action.payload.rtdbInstance}.firebaseio.com`,
});
const app = firebase.initializeApp(
{
databaseURL: `https://${action.payload.rtdbInstance}.firebasedatabase.app`,
},
action.payload.rtdbInstance!,
);
const rtdb = app.database();
yield put(actions.updateRTDB(rtdb));
}
Expand All @@ -96,11 +82,10 @@ export function* initializeRTDB() {
yield takeEvery(loadGameActions.saveGame, executeInitializeRTDB);
}

function* executeStartPolling(action: ReturnType<typeof actions.updateRTDB>) {
yield put(actions.startPollingOnlineStatus());
function* executeStartListen(action: ReturnType<typeof actions.updateRTDB>) {
yield put(actions.startListenToOnlineStatus());
}

export function* startPolling() {
yield takeEvery(actions.updateRTDB, executeStartPolling);
export function* startListen() {
yield takeEvery(actions.updateRTDB, executeStartListen);
}
4 changes: 2 additions & 2 deletions packages/game-app/src/loadBalancer/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const getSlice = createSelector(
state => state[name],
);

const getStatus = createSelector(getSlice, state => state.status);
const getRTDB = createSelector(getSlice, state => state.rtdb);

export const reducer = slice.reducer;
Expand All @@ -50,10 +51,9 @@ export const actions = {
updateOnlineStatus: createAction<'online' | 'offline'>(`${name}/updateOnlineStatus`),
startListenToOnlineStatus: createAction(`${name}/startListenToOnlineStatus`),
stopListenToOnlineStatus: createAction(`${name}/stopListenToOnlineStatus`),
startPollingOnlineStatus: createAction(`${name}/startPollingToOnlineStatus`),
stopPollingOnlineStatus: createAction(`${name}/stopPollingToOnlineStatus`),
};

export const selectors = {
getRTDB,
getStatus,
};

0 comments on commit 93a32de

Please sign in to comment.