vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/twopcz.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package tabletserver 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "html/template" 23 "net/http" 24 25 "vitess.io/vitess/go/vt/vttablet/tabletserver/tx" 26 27 "vitess.io/vitess/go/acl" 28 "vitess.io/vitess/go/vt/log" 29 ) 30 31 var ( 32 gridTable = []byte(`<!DOCTYPE html> 33 <style type="text/css"> 34 table.gridtable { 35 font-family: verdana,arial,sans-serif; 36 font-size: 11px; 37 border-width: 1px; 38 border-collapse: collapse; table-layout:fixed; overflow: hidden; 39 } 40 table.gridtable th { 41 border-width: 1px; 42 padding: 8px; 43 border-style: solid; 44 background-color: #dedede; 45 white-space: nowrap; 46 } 47 table.gridtable td { 48 border-width: 1px; 49 padding: 5px; 50 border-style: solid; 51 } 52 table.gridtable th { 53 padding-left: 2em; 54 padding-right: 2em; 55 } 56 </style> 57 `) 58 startTable = []byte(` 59 <table class="gridtable"> 60 `) 61 endTable = []byte(` 62 </table> 63 `) 64 65 failedzHeader = []byte(` 66 <h3>Failed Transactions</h3> 67 <thead><tr> 68 <th>DTID</th> 69 <th>Queries</th> 70 <th>Time</th> 71 <th>Action</th> 72 </tr></thead> 73 `) 74 failedzRow = template.Must(template.New("failedz").Parse(` 75 <tr> 76 <td>{{.Dtid}}</td> 77 <td>{{range .Queries}}{{.}}<br>{{end}}</td> 78 <td>{{.Time}}</td> 79 <td><form> 80 <input type="hidden" name="dtid" value="{{.Dtid}}"></input> 81 <input type="submit" name="Action" value="Discard"></input> 82 </form></td> 83 </tr> 84 `)) 85 86 preparedzHeader = []byte(` 87 <h3>Prepared Transactions</h3> 88 <thead><tr> 89 <th>DTID</th> 90 <th>Queries</th> 91 <th>Time</th> 92 <th>Action</th> 93 </tr></thead> 94 `) 95 preparedzRow = template.Must(template.New("preparedz").Parse(` 96 <tr> 97 <td>{{.Dtid}}</td> 98 <td>{{range .Queries}}{{.}}<br>{{end}}</td> 99 <td>{{.Time}}</td> 100 <td><form> 101 <input type="hidden" name="dtid" value="{{.Dtid}}"></input> 102 <input type="submit" name="Action" value="Rollback"></input> 103 <input type="submit" name="Action" value="Commit"></input> 104 </form></td> 105 </tr> 106 `)) 107 108 distributedzHeader = []byte(` 109 <h3>Distributed Transactions</h3> 110 <thead><tr> 111 <th>DTID</th> 112 <th>State</th> 113 <th>Time</th> 114 <th>Participants</th> 115 <th>Action</th> 116 </tr></thead> 117 `) 118 distributedzRow = template.Must(template.New("distributedz").Parse(` 119 <tr> 120 <td>{{.Dtid}}</td> 121 <td>{{.State}}</td> 122 <td>{{.Created}}</td> 123 <td>{{range .Participants}}{{.Keyspace}}:{{.Shard}}<br>{{end}}</td> 124 <td><form> 125 <input type="hidden" name="dtid" value="{{.Dtid}}"></input> 126 <input type="submit" name="Action" value="Conclude"></input> 127 </form></td> 128 </tr> 129 `)) 130 ) 131 132 func twopczHandler(txe *TxExecutor, w http.ResponseWriter, r *http.Request) { 133 if err := acl.CheckAccessHTTP(r, acl.DEBUGGING); err != nil { 134 acl.SendError(w, err) 135 return 136 } 137 var err error 138 dtid := r.FormValue("dtid") 139 action := r.FormValue("Action") 140 switch action { 141 case "Discard", "Rollback": 142 err = txe.RollbackPrepared(dtid, 0) 143 case "Commit": 144 err = txe.CommitPrepared(dtid) 145 case "Conclude": 146 err = txe.ConcludeTransaction(dtid) 147 } 148 var msg string 149 if action != "" { 150 if err != nil { 151 msg = fmt.Sprintf("%s(%s): %v", r.FormValue("Action"), dtid, err) 152 } else { 153 msg = fmt.Sprintf("%s(%s): completed.", r.FormValue("Action"), dtid) 154 } 155 } 156 distributed, prepared, failed, err := txe.ReadTwopcInflight() 157 if err != nil { 158 http.Error(w, err.Error(), http.StatusInternalServerError) 159 return 160 } 161 format := r.FormValue("format") 162 if format == "json" { 163 w.Header().Set("Content-Type", "application/json") 164 js, err := json.Marshal(struct { 165 Distributed []*tx.DistributedTx 166 Prepared, Failed []*tx.PreparedTx 167 }{ 168 Distributed: distributed, 169 Prepared: prepared, 170 Failed: failed, 171 }) 172 if err != nil { 173 http.Error(w, err.Error(), http.StatusInternalServerError) 174 return 175 } 176 w.Header().Set("Content-Type", "application/json") 177 w.Write(js) 178 return 179 } 180 181 w.Write(gridTable) 182 w.Write([]byte("<h2>WARNING: Actions on this page can jeopardize data integrity.</h2>\n")) 183 if msg != "" { 184 fmt.Fprintln(w, msg) 185 } 186 187 w.Write(startTable) 188 w.Write(failedzHeader) 189 for _, row := range failed { 190 if err := failedzRow.Execute(w, row); err != nil { 191 log.Errorf("queryz: couldn't execute template: %v", err) 192 } 193 } 194 w.Write(endTable) 195 196 w.Write(startTable) 197 w.Write(preparedzHeader) 198 for _, row := range prepared { 199 if err := preparedzRow.Execute(w, row); err != nil { 200 log.Errorf("queryz: couldn't execute template: %v", err) 201 } 202 } 203 w.Write(endTable) 204 205 w.Write(startTable) 206 w.Write(distributedzHeader) 207 for _, row := range distributed { 208 if err := distributedzRow.Execute(w, row); err != nil { 209 log.Errorf("queryz: couldn't execute template: %v", err) 210 } 211 } 212 w.Write(endTable) 213 }