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

Execution Dashboard Backend Pagination & Frontend Loading Icon #3105

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c1cd1e3
add loading icon while waiting execution data
MiuMiuMiue Nov 14, 2024
095317d
fix execution hyperlink
MiuMiuMiue Nov 15, 2024
702dcdb
remove unnecessary workflow list
MiuMiuMiue Nov 15, 2024
f9e4a72
optimize the query that only send the latest execution for each workflow
MiuMiuMiue Nov 15, 2024
ba9857a
add api function to retrieve the number of executed workflows
MiuMiuMiue Nov 17, 2024
665213b
frontend service function
MiuMiuMiue Nov 17, 2024
8b4e813
remove frontend pagination and put it in the backend
MiuMiuMiue Nov 18, 2024
1272bc8
move sorting to backend
MiuMiuMiue Nov 19, 2024
47ed7a2
filter function
MiuMiuMiue Nov 20, 2024
4f11213
removing hardcode pageSize
MiuMiuMiue Nov 21, 2024
21efcb6
comments added.
MiuMiuMiue Nov 21, 2024
3acada2
update
MiuMiuMiue Nov 21, 2024
1ab8874
frontend format fix
MiuMiuMiue Nov 21, 2024
c2ac1a0
backend format fix
MiuMiuMiue Nov 21, 2024
f01bc94
Merge branch 'master' into Zhe-execution-optimization
MiuMiuMiue Nov 21, 2024
8ecbf34
set min page size to 6
MiuMiuMiue Nov 22, 2024
5eb865d
Merge branch 'master' into Zhe-execution-optimization
MiuMiuMiue Nov 22, 2024
6a57635
Merge branch 'master' into Zhe-execution-optimization
kunwp1 Nov 22, 2024
41706a8
Merge branch 'master' into Zhe-execution-optimization
MiuMiuMiue Nov 22, 2024
b2f4412
make no_sorting constant
MiuMiuMiue Nov 23, 2024
e421501
Merge branch 'Zhe-execution-optimization' of github.com:Texera/texera…
MiuMiuMiue Nov 23, 2024
118011d
merge two queries together
MiuMiuMiue Nov 23, 2024
ab7bf51
add page size changer
MiuMiuMiue Nov 24, 2024
ae05ded
change page index when page size changed if necessary
MiuMiuMiue Nov 24, 2024
182fae5
give more width to end time column
MiuMiuMiue Nov 24, 2024
2ccd0d9
fmt fix
MiuMiuMiue Nov 24, 2024
6944137
make the table scrollable
MiuMiuMiue Nov 24, 2024
2ab7cd6
Merge branch 'master' into Zhe-execution-optimization
MiuMiuMiue Nov 24, 2024
c6c8aa4
Merge branch 'master' into Zhe-execution-optimization
kunwp1 Nov 24, 2024
3bc61f2
refactor backend code
MiuMiuMiue Nov 25, 2024
a1ff9e8
Merge branch 'Zhe-execution-optimization' of github.com:Texera/texera…
MiuMiuMiue Nov 25, 2024
1922dce
Merge branch 'master' into Zhe-execution-optimization
kunwp1 Nov 25, 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
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import edu.uci.ics.texera.web.auth.SessionUser
import edu.uci.ics.texera.web.model.jooq.generated.Tables._
import edu.uci.ics.texera.web.resource.dashboard.admin.execution.AdminExecutionResource._
import io.dropwizard.auth.Auth
import org.jooq.impl.DSL
import org.jooq.types.UInteger

import javax.annotation.security.RolesAllowed
import javax.ws.rs._
import javax.ws.rs.core.MediaType
import scala.jdk.CollectionConverters.IterableHasAsScala
import scala.jdk.CollectionConverters._

/**
* This file handles various request related to saved-executions.
Expand Down Expand Up @@ -45,49 +46,130 @@ object AdminExecutionResource {
}
}

def mapToStatus(status: String): Int = {
status match {
case "READY" => 0
case "RUNNING" => 1
case "PAUSED" => 2
case "COMPLETED" => 3
case "FAILED" => 4
case "KILLED" => 5
case _ => -1 // or throw an exception, depends on your needs
}
}

val sortFieldMapping = Map(
"workflow_name" -> WORKFLOW.NAME,
"execution_name" -> WORKFLOW_EXECUTIONS.NAME,
"initiator" -> USER.NAME,
"end_time" -> WORKFLOW_EXECUTIONS.LAST_UPDATE_TIME
)

}

@Produces(Array(MediaType.APPLICATION_JSON))
@Path("/admin/execution")
@RolesAllowed(Array("ADMIN"))
class AdminExecutionResource {

@GET
@Path("/totalWorkflow")
@Produces()
def getTotalWorkflows: Int = {
context
.select(
DSL.countDistinct(WORKFLOW.WID)
)
.from(WORKFLOW_EXECUTIONS)
.join(WORKFLOW_VERSION)
.on(WORKFLOW_EXECUTIONS.VID.eq(WORKFLOW_VERSION.VID))
.join(USER)
.on(WORKFLOW_EXECUTIONS.UID.eq(USER.UID))
.join(WORKFLOW)
.on(WORKFLOW.WID.eq(WORKFLOW_VERSION.WID))
.fetchOne(0, classOf[Int])
}

/**
* This method retrieves all existing executions
* This method retrieves latest execution of each workflow for specified page.
* The returned executions are sorted and filtered according to the parameters.
*/
@GET
@Path("/executionList")
@Path("/executionList/{pageSize}/{pageIndex}/{sortField}/{sortDirection}")
@Produces(Array(MediaType.APPLICATION_JSON))
def listWorkflows(@Auth current_user: SessionUser): List[dashboardExecution] = {
val workflowEntries = context
def listWorkflows(
@Auth current_user: SessionUser,
@PathParam("pageSize") page_size: Int = 20,
@PathParam("pageIndex") page_index: Int = 0,
@PathParam("sortField") sortField: String = "end_time",
@PathParam("sortDirection") sortDirection: String = "desc",
@QueryParam("filter") filter: java.util.List[String]
): List[dashboardExecution] = {
val filter_status = filter.asScala.map(mapToStatus).toSeq.filter(_ != -1).asJava

// Base query that retrieves latest execution info for each workflow without sorting and filtering.
// Only retrieving executions in current page according to pageSize and pageIndex parameters.
val executions_base_query = context
MiuMiuMiue marked this conversation as resolved.
Show resolved Hide resolved
.select(
WORKFLOW_EXECUTIONS.UID,
USER.NAME,
WORKFLOW_VERSION.WID,
WORKFLOW.NAME,
WORKFLOW_EXECUTIONS.EID,
WORKFLOW_EXECUTIONS.VID,
WORKFLOW_EXECUTIONS.STARTING_TIME,
WORKFLOW_EXECUTIONS.LAST_UPDATE_TIME,
WORKFLOW_EXECUTIONS.STATUS,
WORKFLOW_EXECUTIONS.NAME
)
.from(WORKFLOW_EXECUTIONS)
.leftJoin(WORKFLOW_VERSION)
.join(WORKFLOW_VERSION)
.on(WORKFLOW_EXECUTIONS.VID.eq(WORKFLOW_VERSION.VID))
.leftJoin(USER)
.join(USER)
.on(WORKFLOW_EXECUTIONS.UID.eq(USER.UID))
.leftJoin(WORKFLOW)
.join(WORKFLOW)
.on(WORKFLOW.WID.eq(WORKFLOW_VERSION.WID))
.fetch()
.naturalJoin(
context
.select(
DSL.max(WORKFLOW_EXECUTIONS.EID).as("eid")
)
.from(WORKFLOW_EXECUTIONS)
.join(WORKFLOW_VERSION)
.on(WORKFLOW_VERSION.VID.eq(WORKFLOW_EXECUTIONS.VID))
.groupBy(WORKFLOW_VERSION.WID)
)

// Apply filter if the status are not empty.
val executions_apply_filter = if (!filter_status.isEmpty) {
executions_base_query.where(WORKFLOW_EXECUTIONS.STATUS.in(filter_status))
} else {
executions_base_query
}

// Apply sorting if user specified.
var executions_apply_order =
executions_apply_filter.limit(page_size).offset(page_index * page_size)
if (sortField != "NO_SORTING") {
executions_apply_order = executions_apply_filter
.orderBy(
if (sortDirection == "desc") sortFieldMapping.getOrElse(sortField, WORKFLOW.NAME).desc()
else sortFieldMapping.getOrElse(sortField, WORKFLOW.NAME).asc()
)
.limit(page_size)
.offset(page_index * page_size)
}

val executions = executions_apply_order.fetch()

// Retrieve the id of each workflow that the user has access to.
val availableWorkflowIds = context
.select(WORKFLOW_USER_ACCESS.WID)
.from(WORKFLOW_USER_ACCESS)
.where(WORKFLOW_USER_ACCESS.UID.eq(current_user.getUid))
.fetchInto(classOf[UInteger])

workflowEntries
// Calculate the statistics needed for each execution.
executions
.map(workflowRecord => {
val startingTime =
workflowRecord.get(WORKFLOW_EXECUTIONS.STARTING_TIME).getTime
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,42 @@

<nz-table
#basicTable
[nzData]="listOfExecutions">
nzShowSizeChanger
[nzScroll]="{y: '500px'}"
[nzData]="listOfExecutions"
[nzLoading]="isLoading"
[nzLoadingIndicator]="loadingTemplate"
[nzTemplateMode]="true"
[nzFrontPagination]="false"
[nzTotal]="totalWorkflows"
[nzPageSize]="pageSize"
[nzPageIndex]="currentPageIndex + 1"
[nzPageSizeOptions]="[5, 10, 20, 50]"
(nzQueryParams)="onQueryParamsChange($event)"
class="execution-table">
<thead>
<tr>
<th
[nzSortFn]="sortByWorkflowName"
[nzSortDirections]="['ascend', 'descend']"
[nzShowSort]="true"
[nzSortFn]="true"
[nzSortDirections]="['ascend', 'descend', null]"
(nzSortOrderChange)="onSortChange('workflow_name', $event)"
nzWidth="16%">
Workflow (ID)
</th>
<th
[nzSortFn]="sortByExecutionName"
[nzSortDirections]="['ascend', 'descend']"
[nzShowSort]="true"
[nzSortFn]="true"
[nzSortDirections]="['ascend', 'descend', null]"
(nzSortOrderChange)="onSortChange('execution_name', $event)"
nzWidth="16%">
Execution Name (ID)
</th>
<th
[nzSortFn]="sortByInitiator"
[nzSortDirections]="['ascend', 'descend']"
[nzShowSort]="true"
[nzSortFn]="true"
[nzSortDirections]="['ascend', 'descend', null]"
(nzSortOrderChange)="onSortChange('initiator', $event)"
nzWidth="12%">
Initiator
</th>
Expand All @@ -42,25 +60,28 @@
{ text: 'KILLED', value: 'KILLED'},
{ text: 'JUST COMPLETED', value: 'JUST COMPLETED'},
{ text: 'UNKNOWN', value: 'UNKNOWN'}]"
[nzFilterFn]="filterByStatus"
nzWidth="16%">
[nzFilterFn]="true"
(nzFilterChange)="onFilterChange($event)"
nzWidth="13%">
Status
</th>
<th nzWidth="10%">Time Used (hh:mm:ss)</th>
<th
[nzSortFn]="sortByCompletedTime"
[nzSortDirections]="['ascend', 'descend']"
nzWidth="15%">
[nzShowSort]="true"
[nzSortFn]="true"
[nzSortDirections]="['ascend', 'descend', null]"
(nzSortOrderChange)="onSortChange('end_time', $event)"
nzWidth="20%">
End Time
</th>
<th nzWidth="15%">Action</th>
<th nzWidth="13%">Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let execution of basicTable.data">
<td>
<div *ngIf="execution.access; else normalWorkflowName">
<a href="/workflow/{{execution.workflowId}}">
<a href="/dashboard/user/workspace/{{execution.workflowId}}">
{{ maxStringLength(execution.workflowName, 16) }} ({{ execution.workflowId }})
</a>
</div>
Expand Down Expand Up @@ -126,7 +147,7 @@
nzType="redo"></i>
</button>
<button
(click)="clickToViewHistory(execution.workflowId)"
(click)="clickToViewHistory(execution.workflowId, execution.workflowName)"
nz-button
nz-tooltip="previous execution of the workflow: {{
execution.workflowName
Expand All @@ -141,3 +162,12 @@
</tr>
</tbody>
</nz-table>

<ng-template #loadingTemplate>
<div class="loading-container">
<nz-spin
nzTip="Loading..."
[nzSpinning]="true"
nzSize="large"></nz-spin>
</div>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.loading-container {
display: flex;
justify-content: center;
flex-direction: column;
height: 300px;
}

.execution-table {
display: block;
grid-row-start: 3;
grid-row-end: 4;
}
Loading
Loading