mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-10-09 12:27:52 +00:00
add transactions external documentation
- make sure client reads/closes all bodies - support blank transaction ID in http handler
This commit is contained in:
parent
210c7239ab
commit
41975de6b8
4 changed files with 106 additions and 6 deletions
|
|
@ -1247,7 +1247,10 @@ func (c *InternalClient) Transactions(ctx context.Context) (map[string]pilosa.Tr
|
|||
if err != nil {
|
||||
return trnsMap, errors.Wrap(err, "executing request")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() {
|
||||
io.Copy(ioutil.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
}()
|
||||
tmpTrnsMap := make(map[string]*pilosa.Transaction)
|
||||
err = json.NewDecoder(resp.Body).Decode(&tmpTrnsMap)
|
||||
|
||||
|
|
@ -1288,7 +1291,10 @@ func (c *InternalClient) StartTransaction(ctx context.Context, id string, timeou
|
|||
if err != nil {
|
||||
return pilosa.Transaction{}, errors.Wrap(err, "executing request")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() {
|
||||
io.Copy(ioutil.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
}()
|
||||
err = json.NewDecoder(resp.Body).Decode(&tr)
|
||||
if err != nil {
|
||||
return pilosa.Transaction{}, errors.Wrap(err, "decoding response")
|
||||
|
|
@ -1318,7 +1324,10 @@ func (c *InternalClient) FinishTransaction(ctx context.Context, id string) (pilo
|
|||
if err != nil {
|
||||
return pilosa.Transaction{}, errors.Wrap(err, "executing request")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() {
|
||||
io.Copy(ioutil.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
}()
|
||||
tr := &TransactionResponse{Transaction: &pilosa.Transaction{}}
|
||||
err = json.NewDecoder(resp.Body).Decode(&tr)
|
||||
if err != nil {
|
||||
|
|
@ -1351,7 +1360,10 @@ func (c *InternalClient) GetTransaction(ctx context.Context, id string) (pilosa.
|
|||
if err != nil {
|
||||
return pilosa.Transaction{}, errors.Wrap(err, "executing request")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() {
|
||||
io.Copy(ioutil.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
}()
|
||||
tr := &TransactionResponse{Transaction: &pilosa.Transaction{}}
|
||||
err = json.NewDecoder(resp.Body).Decode(&tr)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -1386,6 +1386,20 @@ func TestClientTransactions(t *testing.T) {
|
|||
pilosa.Transaction{},
|
||||
trns)
|
||||
}
|
||||
|
||||
// start transaction with blank id
|
||||
if trns, err := client0.StartTransaction(context.Background(), "", time.Minute, false); err != nil {
|
||||
t.Fatalf("error starting transaction: %v", err)
|
||||
} else {
|
||||
expDeadline = time.Now().Add(time.Minute)
|
||||
if len(trns.ID) != 36 {
|
||||
t.Errorf("expected generated UUID, but got '%s'", trns.ID)
|
||||
}
|
||||
test.CompareTransactions(t,
|
||||
pilosa.Transaction{ID: trns.ID, Timeout: time.Minute, Active: true, Deadline: expDeadline},
|
||||
trns)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Client represents a test wrapper for pilosa.Client.
|
||||
|
|
|
|||
|
|
@ -343,6 +343,8 @@ func newRouter(handler *Handler) *mux.Router {
|
|||
router.HandleFunc("/version", handler.handleGetVersion).Methods("GET").Name("GetVersion")
|
||||
router.HandleFunc("/transactions", handler.handleGetTransactions).Methods("GET").Name("GetTransactions")
|
||||
router.HandleFunc("/transaction/{id}", handler.handleGetTransaction).Methods("GET").Name("GetTransaction")
|
||||
router.HandleFunc("/transaction/", handler.handlePostTransaction).Methods("POST").Name("PostTransaction")
|
||||
router.HandleFunc("/transaction", handler.handlePostTransaction).Methods("POST").Name("PostTransaction")
|
||||
router.HandleFunc("/transaction/{id}", handler.handlePostTransaction).Methods("POST").Name("PostTransaction")
|
||||
router.HandleFunc("/transaction/{id}/finish", handler.handlePostFinishTransaction).Methods("POST").Name("PostFinishTransaction")
|
||||
|
||||
|
|
@ -1124,7 +1126,10 @@ func (h *Handler) handlePostTransaction(w http.ResponseWriter, r *http.Request)
|
|||
return
|
||||
}
|
||||
|
||||
id := mux.Vars(r)["id"]
|
||||
id, ok := mux.Vars(r)["id"]
|
||||
if !ok {
|
||||
id = reqTrns.ID
|
||||
}
|
||||
trns, err := h.api.StartTransaction(id, reqTrns.Timeout, reqTrns.Exclusive, false)
|
||||
|
||||
h.doTransactionResponse(w, err, trns)
|
||||
|
|
|
|||
|
|
@ -159,11 +159,13 @@ goes through API (and is passed directly to Server). (unimplemented)
|
|||
|
||||
#### TODO
|
||||
|
||||
- [x] implement api layer and cluster logic, startup, etc.
|
||||
- [x] implement api layer and cluster logic, startup, etc.
|
||||
- [ ] add new cluster state to explicitly reject certain requests during exclusive transaction?
|
||||
- [ ] implement HTTP layer including header/transaction ID
|
||||
- [ ] implement and use persistent transaction store rather than inmem.
|
||||
- [ ] update go-pilosa/gpexp to actually USE transactions
|
||||
- [ ] update IDK to use updated go-pilosa
|
||||
- [ ] external testing with e.g. curl
|
||||
|
||||
- ID validation. No slashes, no non-URL safe chars
|
||||
|
||||
|
|
@ -172,3 +174,70 @@ goes through API (and is passed directly to Server). (unimplemented)
|
|||
- if the Exclusive transaction is active, there should be no other transactions
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
Before performing a backup, you must request an exclusive "transaction" with the cluster. Do this via and HTTP POST to the coordinator node at path:
|
||||
|
||||
`/transaction` OR `/transaction/{id}` if you wish to specify a custom ID (any alphanum+dash). Otherwise a UUID will be generated and returned in the response.
|
||||
|
||||
Use headers:
|
||||
|
||||
```
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
And body like:
|
||||
|
||||
```
|
||||
{
|
||||
"timeout": "10m",
|
||||
"exclusive": true
|
||||
}
|
||||
```
|
||||
|
||||
You may choose any timeout you like, though it's better to err on the
|
||||
longer side of how long you expect the backup to take. You explicitly
|
||||
finish the transaction once you're done, so the timeout exists solely
|
||||
for cleanup in the case of failures.
|
||||
|
||||
This will return a JSON "transaction response" object.
|
||||
```
|
||||
{
|
||||
"transaction": {
|
||||
"id":"5e572d95-4204-40cd-804c-92976b68dc9b",
|
||||
"active":true,
|
||||
"exclusive":false,
|
||||
"timeout":"1m0s",
|
||||
"deadline":"2020-04-17T21:54:18.69359-05:00"
|
||||
},
|
||||
"error":"some message"
|
||||
}
|
||||
```
|
||||
|
||||
The `error` field MAY not be present if there is no error.
|
||||
|
||||
You MUST check whether `active` is true. If not, you must poll the transaction endpoint with a GET request and your ID until it is true. This looks like:
|
||||
|
||||
GET `/transaction/5e572d95-4204-40cd-804c-92976b68dc9b`
|
||||
|
||||
with headers:
|
||||
|
||||
```
|
||||
Accept: application/json
|
||||
```
|
||||
|
||||
and also returns a "transaction response" object.
|
||||
|
||||
Once an "active", "exclusive" transaction is returned, proceed with your backup.
|
||||
|
||||
Once the backup is complete, finish the transaction with
|
||||
|
||||
POST `/transaction/{id}/finish`
|
||||
|
||||
with headers:
|
||||
|
||||
```
|
||||
Accept: application/json
|
||||
```
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue