transactions as a list endpoint; added Transaction.CreatedAt

This commit is contained in:
Travis 2020-08-12 14:09:57 -05:00
parent 0dea018fb7
commit 1837811ce1
No known key found for this signature in database
GPG key ID: 37080CC2042BA34E
3 changed files with 53 additions and 2 deletions

View file

@ -32,6 +32,7 @@ import (
"reflect"
"runtime/debug"
"runtime/pprof"
"sort"
"strconv"
"strings"
"sync"
@ -220,6 +221,7 @@ func (h *Handler) populateValidators() {
h.validators["PostFieldAttrDiff"] = queryValidationSpecRequired()
h.validators["GetNodes"] = queryValidationSpecRequired()
h.validators["GetShardMax"] = queryValidationSpecRequired()
h.validators["GetTransactionList"] = queryValidationSpecRequired()
h.validators["GetTransactions"] = queryValidationSpecRequired()
h.validators["GetTransaction"] = queryValidationSpecRequired()
h.validators["PostTransaction"] = queryValidationSpecRequired()
@ -362,6 +364,8 @@ func newRouter(handler *Handler) *mux.Router {
router.HandleFunc("/schema", handler.handleGetSchema).Methods("GET").Name("GetSchema")
router.HandleFunc("/schema", handler.handlePostSchema).Methods("POST").Name("PostSchema")
router.HandleFunc("/status", handler.handleGetStatus).Methods("GET").Name("GetStatus")
router.HandleFunc("/transaction", handler.handleGetTransactionList).Methods("GET").Name("GetTransactionList")
router.HandleFunc("/transaction/", handler.handleGetTransactionList).Methods("GET").Name("GetTransactionList")
router.HandleFunc("/transaction", handler.handlePostTransaction).Methods("POST").Name("PostTransaction")
router.HandleFunc("/transaction/", handler.handlePostTransaction).Methods("POST").Name("PostTransaction")
router.HandleFunc("/transaction/{id}", handler.handleGetTransaction).Methods("GET").Name("GetTransaction")
@ -1241,6 +1245,41 @@ func (h *Handler) handleDeleteField(w http.ResponseWriter, r *http.Request) {
resp.write(w, err)
}
func (h *Handler) handleGetTransactionList(w http.ResponseWriter, r *http.Request) {
if !validHeaderAcceptJSON(r.Header) {
http.Error(w, "JSON only acceptable response", http.StatusNotAcceptable)
return
}
trnsMap, err := h.api.Transactions(r.Context())
if err != nil {
switch errors.Cause(err) {
case pilosa.ErrNodeNotCoordinator:
http.Error(w, err.Error(), http.StatusBadRequest)
default:
http.Error(w, "problem getting transactions: "+err.Error(), http.StatusInternalServerError)
}
return
}
// Convert the map of transactions to a slice.
trnsList := make([]*pilosa.Transaction, len(trnsMap))
var i int
for _, v := range trnsMap {
trnsList[i] = v
i++
}
// Sort the slice by createdAt.
sort.Slice(trnsList, func(i, j int) bool {
return trnsList[i].CreatedAt.Before(trnsList[j].CreatedAt)
})
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(trnsList); err != nil {
h.logger.Printf("encoding GetTransactionList response: %s", err)
}
}
func (h *Handler) handleGetTransactions(w http.ResponseWriter, r *http.Request) {
if !validHeaderAcceptJSON(r.Header) {
http.Error(w, "JSON only acceptable response", http.StatusNotAcceptable)

View file

@ -45,6 +45,10 @@ type Transaction struct {
// Timeout is the minimum idle time for which this transaction should continue to exist.
Timeout time.Duration `json:"timeout"`
// CreatedAt is the timestamp at which the transaction was created. This supports
// the case of listing transactions in a useful order.
CreatedAt time.Time `json:"createdAt"`
// Deadline is calculated from Timeout. TODO reset deadline each time there is activity
// on the transaction. (we can't do this until there is some method of associating a
// request/call with a transaction)
@ -130,12 +134,14 @@ func (tm *TransactionManager) Start(ctx context.Context, id string, timeout time
active := !exclusive || (len(trnsMap) == 0)
// set deadline according to timeout
deadline := time.Now().Add(timeout)
createdAt := time.Now()
deadline := createdAt.Add(timeout)
trns := &Transaction{
ID: id,
Active: active,
Exclusive: exclusive,
Timeout: timeout,
CreatedAt: createdAt,
Deadline: deadline,
}
if err = tm.store.Put(trns); err != nil {
@ -447,12 +453,14 @@ func (trns *Transaction) MarshalJSON() ([]byte, error) {
Active bool `json:"active"`
Exclusive bool `json:"exclusive"`
Timeout string `json:"timeout"`
CreatedAt string `json:"createdAt"`
Deadline string `json:"deadline"`
}{
ID: trns.ID,
Active: trns.Active,
Exclusive: trns.Exclusive,
Timeout: trns.Timeout.String(),
CreatedAt: trns.CreatedAt.In(time.UTC).Format(time.RFC3339Nano),
Deadline: trns.Deadline.In(time.UTC).Format(time.RFC3339Nano),
})
}
@ -463,6 +471,7 @@ func (trns *Transaction) Copy() *Transaction {
Active: trns.Active,
Exclusive: trns.Exclusive,
Timeout: trns.Timeout,
CreatedAt: trns.CreatedAt,
Deadline: trns.Deadline,
Stats: trns.Stats,
}

View file

@ -28,7 +28,9 @@ presumably cluster-wide.
The base transaction endpoints are `/transactions`, for listing or creating
transactions, and `/transaction/[id]`, for listing, creating, finishing, or
cancelling a transaction.
cancelling a transaction. A GET to the `/transaction` endpoint (without an
`[id]`) will return a list of all transactions ordered by their creation
date: `createdAt`.
A POST to `/transaction` attempts to create a transaction, assigning it an
arbitrary ID that is not the ID of any existing transaction. A `GET` from
@ -214,6 +216,7 @@ This will return a JSON "transaction response" object.
"active":true,
"exclusive":false,
"timeout":"1m0s",
"createdAt":"2020-04-17T21:53:18.69359-05:00",
"deadline":"2020-04-17T21:54:18.69359-05:00"
},
"error":"some message"