Skip to content

Commit

Permalink
[Code] Remove socket.io and use polling message to pull progresses (#…
Browse files Browse the repository at this point in the history
…31398)

* [Code] Remove socket.io and use polling message to pull progresses

* [Code] refactor the status polling logic

* [Code] fix a minor test issue
  • Loading branch information
mw-ding authored Feb 22, 2019
1 parent fda5073 commit a4cc1d1
Show file tree
Hide file tree
Showing 23 changed files with 254 additions and 528 deletions.
4 changes: 0 additions & 4 deletions x-pack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@
"@types/redux-actions": "^2.2.1",
"@types/rimraf": "^2.0.2",
"@types/sinon": "^7.0.0",
"@types/socket.io": "^1.4.38",
"@types/socket.io-client": "^1.4.32",
"@types/styled-components": "^3.0.1",
"@types/storybook__addon-actions": "^3.4.1",
"@types/storybook__addon-info": "^3.4.2",
Expand Down Expand Up @@ -304,8 +302,6 @@
"rison-node": "0.3.1",
"rxjs": "^6.2.1",
"semver": "5.1.0",
"socket.io": "^2.1.1",
"socket.io-client": "^2.1.1",
"squel": "^5.12.2",
"stats-lite": "^2.2.0",
"style-it": "2.1.2",
Expand Down
4 changes: 4 additions & 0 deletions x-pack/plugins/code/public/actions/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export const loadStatus = createAction<string>('LOAD STATUS');
export const loadStatusSuccess = createAction<any>('LOAD STATUS SUCCESS');
export const loadStatusFailed = createAction<string>('LOAD STATUS FAILED');

export const pollRepoCloneStatus = createAction<any>('POLL CLONE STATUS');
export const pollRepoIndexStatus = createAction<any>('POLL INDEX STATUS');
export const pollRepoDeleteStatus = createAction<any>('POLL DELETE STATUS');

export const loadRepo = createAction<string>('LOAD REPO');
export const loadRepoSuccess = createAction<any>('LOAD REPO SUCCESS');
export const loadRepoFailed = createAction<any>('LOAD REPO FAILED');
Expand Down
4 changes: 0 additions & 4 deletions x-pack/plugins/code/public/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,8 @@ import chrome from 'ui/chrome';
// @ts-ignore
import { uiModules } from 'ui/modules';
import { App } from './components/app';
import { bindSocket } from './socket';
import { store } from './stores';

// Bind the web socket client.
bindSocket(store);

const app = uiModules.get('apps/code');

app.config(($locationProvider: any) => {
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/code/public/components/main/content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { RepoState, RepoStatus, RootState } from '../../reducers';
import {
currentTreeSelector,
hasMoreCommitsSelector,
repoUriSelector,
statusSelector,
treeCommitsSelector,
} from '../../selectors';
Expand Down Expand Up @@ -374,7 +375,7 @@ const mapStateToProps = (state: RootState) => ({
commits: treeCommitsSelector(state),
hasMoreCommits: hasMoreCommitsSelector(state),
loadingCommits: state.file.loadingCommits,
repoStatus: statusSelector(state),
repoStatus: statusSelector(state, repoUriSelector(state)),
repoScope: state.search.searchOptions.repoScope.map(r => r.uri),
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import React from 'react';
import { MemoryRouter } from 'react-router';
import { MemoryRouter } from 'react-router-dom';
import renderer from 'react-test-renderer';
import { mockFunction } from '../../utils/test_utils';
import { CodeSymbolTree } from './code_symbol_tree';
Expand Down
11 changes: 10 additions & 1 deletion x-pack/plugins/code/public/sagas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ import {
import { watchFetchBranchesAndCommits, watchFetchRepoTree, watchRepoRouteChange } from './file';
import { watchInstallLanguageServer, watchLoadLanguageServers } from './language_server';
import { watchLoadConfigs, watchSwitchProjectLanguageServer } from './project_config';
import { watchLoadRepoListStatus, watchLoadRepoStatus } from './project_status';
import {
watchLoadRepoListStatus,
watchLoadRepoStatus,
watchRepoCloneStatusPolling,
watchRepoDeleteStatusPolling,
watchRepoIndexStatusPolling,
} from './project_status';
import {
watchAdminRouteChange,
watchDeleteRepo,
Expand Down Expand Up @@ -68,4 +74,7 @@ export function* rootSaga() {
yield fork(watchLoadConfigs);
yield fork(watchLoadRepoListStatus);
yield fork(watchLoadRepoStatus);
yield fork(watchRepoDeleteStatusPolling);
yield fork(watchRepoIndexStatusPolling);
yield fork(watchRepoCloneStatusPolling);
}
216 changes: 203 additions & 13 deletions x-pack/plugins/code/public/sagas/project_status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,38 @@
*/

import { Action } from 'redux-actions';
import { all, call, put, takeEvery } from 'redux-saga/effects';
import { delay } from 'redux-saga';
import { all, call, put, takeEvery, takeLatest } from 'redux-saga/effects';
import { kfetch } from 'ui/kfetch';
import { Repository } from '../../model';
import { fetchReposSuccess } from '../actions';
import { loadRepoSuccess, loadStatusFailed, loadStatusSuccess } from '../actions';

import { RepositoryUtils } from '../../common/repository_utils';
import { Repository, RepositoryUri, WorkerReservedProgress } from '../../model';
import {
deleteRepo,
fetchReposSuccess,
importRepo,
indexRepo,
loadRepoSuccess,
loadStatusFailed,
loadStatusSuccess,
pollRepoCloneStatus,
pollRepoDeleteStatus,
pollRepoIndexStatus,
updateCloneProgress,
updateDeleteProgress,
updateIndexProgress,
} from '../actions';
import { cloneCompletedPattern } from './status';

function fetchStatus(repoUri: string) {
return kfetch({
pathname: `../api/code/repo/status/${repoUri}`,
});
}

function* loadRepoListStatus(action: Action<Repository[]>) {
function* loadRepoListStatus(repos: Repository[]) {
try {
const repositories = action.payload!;
const promises = repositories.map(repo => call(fetchStatus, repo.uri));
const promises = repos.map(repo => call(fetchStatus, repo.uri));
const statuses = yield all(promises);
yield put(
loadStatusSuccess(
Expand All @@ -35,24 +51,198 @@ function* loadRepoListStatus(action: Action<Repository[]>) {
}
}

function* loadRepoStatus(action: Action<any>) {
function* loadRepoStatus(repo: Repository) {
try {
const repository = action.payload!;
const repoStatus = yield call(fetchStatus, repository.uri);
const repoStatus = yield call(fetchStatus, repo.uri);
yield put(
loadStatusSuccess({
[repository.uri]: repoStatus,
[repo.uri]: repoStatus,
})
);
} catch (err) {
yield put(loadStatusFailed(err));
}
}

function* handleRepoStatus(action: Action<any>) {
const repository: Repository = action.payload!;
yield call(loadRepoStatus, repository);
}

function* handleRepoListStatus(action: Action<Repository[]>) {
const repos: Repository[] = action.payload!;
yield call(loadRepoListStatus, repos);
}

function isInProgress(progress: number): boolean {
return progress < WorkerReservedProgress.COMPLETED && progress >= WorkerReservedProgress.INIT;
}

function* handleRepoListStatusLoaded(action: Action<any>) {
const statuses = action.payload;
for (const repoUri of Object.keys(statuses)) {
const status = statuses[repoUri];
if (status.deleteStatus) {
yield put(pollRepoDeleteStatus(repoUri));
} else if (status.indexStatus) {
if (isInProgress(status.indexStatus.progress)) {
yield put(pollRepoIndexStatus(repoUri));
}
} else if (status.gitStatus) {
if (isInProgress(status.gitStatus.progress)) {
yield put(pollRepoCloneStatus(repoUri));
}
}
}
}

// `fetchReposSuccess` is issued by the repository admin page.
export function* watchLoadRepoListStatus() {
yield takeEvery(String(fetchReposSuccess), loadRepoListStatus);
yield takeEvery(String(fetchReposSuccess), handleRepoListStatus);
// After all the status of all the repositoriesin the list has been loaded,
// start polling status only for those still in progress.
yield takeEvery(String(loadStatusSuccess), handleRepoListStatusLoaded);
}

// `loadRepoSuccess` is issued by the main source view page.
export function* watchLoadRepoStatus() {
yield takeEvery(String(loadRepoSuccess), loadRepoStatus);
yield takeLatest(String(loadRepoSuccess), handleRepoStatus);
}

const REPO_STATUS_POLLING_FREQ_MS = 1000;

function createRepoStatusPollingHandler(
parseRepoUri: (_: Action<any>) => RepositoryUri,
handleStatus: any,
pollingActionFunction: any
) {
return function*(a: Action<any>) {
yield call(delay, REPO_STATUS_POLLING_FREQ_MS);

const repoUri = parseRepoUri(a);
let keepPolling = false;
try {
const repoStatus = yield call(fetchStatus, repoUri);
keepPolling = yield handleStatus(repoStatus, repoUri);
} catch (err) {
// Fetch repository status error. Ignore and keep trying.
keepPolling = true;
}

if (keepPolling) {
yield put(pollingActionFunction(repoUri));
}
};
}

const handleRepoCloneStatusPolling = createRepoStatusPollingHandler(
(action: Action<any>) => {
if (action.type === String(importRepo)) {
const repoUrl: string = action.payload;
return RepositoryUtils.buildRepository(repoUrl).uri;
} else if (action.type === String(pollRepoCloneStatus)) {
return action.payload;
}
},
function*(status: any, repoUri: RepositoryUri) {
if (status.gitStatus) {
const { progress, cloneProgress } = status.gitStatus;
yield put(
updateCloneProgress({
progress,
repoUri,
cloneProgress,
})
);
// Keep polling if the progress is not 100% yet.
return isInProgress(progress);
} else {
// Keep polling if the indexStatus has not been persisted yet.
return true;
}
},
pollRepoCloneStatus
);

export function* watchRepoCloneStatusPolling() {
// The repository clone status polling will be triggered by:
// * user click import repository
// * repeating pollRepoCloneStatus action by the poller itself.
yield takeEvery([String(importRepo), String(pollRepoCloneStatus)], handleRepoCloneStatusPolling);
}

const handleRepoIndexStatusPolling = createRepoStatusPollingHandler(
(action: Action<any>) => {
if (action.type === String(indexRepo) || action.type === String(pollRepoIndexStatus)) {
return action.payload;
} else if (action.type === String(updateCloneProgress)) {
return action.payload.repoUri;
}
},
function*(status: any, repoUri: RepositoryUri) {
if (status.indexStatus) {
yield put(
updateIndexProgress({
progress: status.indexStatus.progress,
repoUri,
})
);
// Keep polling if the progress is not 100% yet.
return isInProgress(status.indexStatus.progress);
} else {
// Keep polling if the indexStatus has not been persisted yet.
return true;
}
},
pollRepoIndexStatus
);

export function* watchRepoIndexStatusPolling() {
// The repository index status polling will be triggered by:
// * user click index repository
// * clone is done
// * repeating pollRepoIndexStatus action by the poller itself.
yield takeEvery(
[String(indexRepo), cloneCompletedPattern, String(pollRepoIndexStatus)],
handleRepoIndexStatusPolling
);
}

const handleRepoDeleteStatusPolling = createRepoStatusPollingHandler(
(action: Action<any>) => {
return action.payload;
},
function*(status: any, repoUri: RepositoryUri) {
if (!status.gitStatus && !status.indexStatus && !status.deleteStatus) {
// If all the statuses cannot be found, this indicates the the repository has been successfully
// removed.
yield put(
updateDeleteProgress({
progress: WorkerReservedProgress.COMPLETED,
repoUri,
})
);
}

if (status.deleteStatus) {
yield put(
updateDeleteProgress({
progress: status.deleteStatus.progress,
repoUri,
})
);
return isInProgress(status.deleteStatus.progress);
}
},
pollRepoDeleteStatus
);

export function* watchRepoDeleteStatusPolling() {
// The repository delete status polling will be triggered by:
// * user click delete repository
// * repeating pollRepoDeleteStatus action by the poller itself.
yield takeEvery(
[String(deleteRepo), String(pollRepoDeleteStatus)],
handleRepoDeleteStatusPolling
);
}
8 changes: 4 additions & 4 deletions x-pack/plugins/code/public/sagas/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ import { RootState } from '../reducers';

const matchSelector = (state: RootState) => state.route.match;

const pattern = (action: Action<any>) =>
export const cloneCompletedPattern = (action: Action<any>) =>
action.type === String(updateCloneProgress) &&
action.payload.progress === WorkerReservedProgress.COMPLETED;

const deletePattern = (action: Action<any>) =>
const deleteCompletedPattern = (action: Action<any>) =>
action.type === String(updateDeleteProgress) &&
action.payload.progress === WorkerReservedProgress.COMPLETED;

Expand All @@ -35,13 +35,13 @@ function* handleRepoCloneSuccess() {
}

export function* watchRepoCloneSuccess() {
yield takeEvery(pattern, handleRepoCloneSuccess);
yield takeEvery(cloneCompletedPattern, handleRepoCloneSuccess);
}

function* handleRepoDeleteFinished(action: any) {
yield put(deleteRepoFinished(action.payload.repoUri));
}

export function* watchRepoDeleteFinished() {
yield takeEvery(deletePattern, handleRepoDeleteFinished);
yield takeEvery(deleteCompletedPattern, handleRepoDeleteFinished);
}
6 changes: 3 additions & 3 deletions x-pack/plugins/code/public/selectors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { FileTree } from '../../model';
import { FileTree, RepositoryUri } from '../../model';
import { RootState } from '../reducers';

export const getTree = (state: RootState) => state.file.tree;
Expand Down Expand Up @@ -35,8 +35,8 @@ export const repoUriSelector = (state: RootState) => {
return `${resource}/${org}/${repo}`;
};

export const statusSelector = (state: RootState) => {
return state.status.status[repoUriSelector(state)];
export const statusSelector = (state: RootState, repoUri: RepositoryUri) => {
return state.status.status[repoUri];
};

export const treeCommitsSelector = (state: RootState) => {
Expand Down
Loading

0 comments on commit a4cc1d1

Please sign in to comment.