mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 02:44:59 +00:00
148 lines
3.8 KiB
Go
148 lines
3.8 KiB
Go
package systemlayer
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
pilosa "github.com/featurebasedb/featurebase/v3"
|
|
)
|
|
|
|
const (
|
|
keepLastRequests = 2000
|
|
truncateTextAt = 4096
|
|
)
|
|
|
|
// ExecutionRequests is an internal struct that keeps a list of sql execution requests
|
|
// this data allows visbility into queries that have been run and are running
|
|
type ExecutionRequests struct {
|
|
sync.RWMutex
|
|
// requestsList being used a FIFO queue here
|
|
// to track both number of stored requests and
|
|
// which one to delete next (always 0)
|
|
requestsList []string
|
|
curIdx int
|
|
// the actual rquests being stored - we use a map
|
|
// so we can look them up by request id which is a uuid
|
|
requests map[string]*pilosa.ExecutionRequest
|
|
}
|
|
|
|
// Ensure type implements interface.
|
|
var _ pilosa.ExecutionRequestsAPI = (*ExecutionRequests)(nil)
|
|
|
|
func NewExecutionRequestsAPI() *ExecutionRequests {
|
|
return &ExecutionRequests{
|
|
requestsList: make([]string, keepLastRequests),
|
|
requests: make(map[string]*pilosa.ExecutionRequest),
|
|
}
|
|
}
|
|
|
|
// AddRequest adds a new request to the ExecutionRequests struct
|
|
func (e *ExecutionRequests) AddRequest(requestID string, userID string, startTime time.Time, sql string) error {
|
|
e.Lock()
|
|
defer e.Unlock()
|
|
|
|
_, ok := e.requests[requestID]
|
|
if ok {
|
|
return fmt.Errorf("request %s already exists", requestID)
|
|
}
|
|
|
|
// we're going to add a new request so clear out oldest request if we've hit
|
|
// the threshhold
|
|
if e.requestsList[e.curIdx] != "" {
|
|
// get the oldest request and delete it
|
|
delId := e.requestsList[e.curIdx]
|
|
delete(e.requests, delId)
|
|
}
|
|
// add the request to the end
|
|
e.requestsList[e.curIdx] = requestID
|
|
e.curIdx = (e.curIdx + 1) % keepLastRequests
|
|
|
|
// update the request
|
|
if len(sql) > truncateTextAt {
|
|
// truncate to 4k, if we need more later, we'll worry about
|
|
// that later the song and dance with the copy is necessary so
|
|
// that the original sql string can actually get garbage
|
|
// collected, otherwise we might retain a reference to the
|
|
// whole thing
|
|
sqlb := make([]byte, truncateTextAt)
|
|
copy(sqlb, []byte(sql))
|
|
sql = string(sqlb)
|
|
}
|
|
e.requests[requestID] = &pilosa.ExecutionRequest{
|
|
RequestID: requestID,
|
|
UserID: userID,
|
|
StartTime: startTime,
|
|
Status: "running",
|
|
SQL: sql,
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// UpdateRequest updates the values for a request in the ExecutionRequests struct
|
|
func (e *ExecutionRequests) UpdateRequest(requestID string,
|
|
endTime time.Time,
|
|
status string,
|
|
waitType string,
|
|
waitTime time.Duration,
|
|
waitResource string,
|
|
cpuTime time.Duration,
|
|
reads int64,
|
|
writes int64,
|
|
logicalReads int64,
|
|
rowCount int64,
|
|
plan string) error {
|
|
|
|
e.Lock()
|
|
defer e.Unlock()
|
|
|
|
request, ok := e.requests[requestID]
|
|
if !ok {
|
|
return fmt.Errorf("request %s not found", requestID)
|
|
}
|
|
if len(plan) > truncateTextAt {
|
|
planb := make([]byte, truncateTextAt)
|
|
copy(planb, []byte(plan))
|
|
plan = string(planb)
|
|
}
|
|
request.EndTime = endTime
|
|
request.Status = status
|
|
request.WaitType = waitType
|
|
request.WaitTime += waitTime
|
|
request.WaitResource = waitResource
|
|
request.CPUTime += cpuTime
|
|
request.Reads += reads
|
|
request.Writes += writes
|
|
request.LogicalReads += logicalReads
|
|
request.RowCount += rowCount
|
|
request.Plan = plan
|
|
|
|
return nil
|
|
}
|
|
|
|
// ListRequests returns the content of the ExecutionRequests struct as copies
|
|
func (e *ExecutionRequests) ListRequests() ([]pilosa.ExecutionRequest, error) {
|
|
e.RLock()
|
|
defer e.RUnlock()
|
|
|
|
result := make([]pilosa.ExecutionRequest, len(e.requests))
|
|
|
|
idx := 0
|
|
for _, er := range e.requests {
|
|
result[idx] = er.Copy()
|
|
idx++
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (e *ExecutionRequests) GetRequest(requestID string) (pilosa.ExecutionRequest, error) {
|
|
e.RLock()
|
|
defer e.RUnlock()
|
|
|
|
er, ok := e.requests[requestID]
|
|
if !ok {
|
|
return pilosa.ExecutionRequest{}, fmt.Errorf("request %s not found", requestID)
|
|
}
|
|
|
|
return er.Copy(), nil
|
|
}
|