github.com/weaviate/weaviate@v1.24.6/adapters/clients/cluster_schema.go (about) 1 // _ _ 2 // __ _____ __ ___ ___ __ _| |_ ___ 3 // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ 4 // \ V V / __/ (_| |\ V /| | (_| | || __/ 5 // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| 6 // 7 // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. 8 // 9 // CONTACT: hello@weaviate.io 10 // 11 12 package clients 13 14 import ( 15 "bytes" 16 "context" 17 "encoding/json" 18 "fmt" 19 "io" 20 "net/http" 21 "net/url" 22 23 "github.com/weaviate/weaviate/usecases/cluster" 24 ) 25 26 type ClusterSchema struct { 27 client *http.Client 28 } 29 30 func NewClusterSchema(httpClient *http.Client) *ClusterSchema { 31 return &ClusterSchema{client: httpClient} 32 } 33 34 func (c *ClusterSchema) OpenTransaction(ctx context.Context, host string, 35 tx *cluster.Transaction, 36 ) error { 37 path := "/schema/transactions/" 38 method := http.MethodPost 39 url := url.URL{Scheme: "http", Host: host, Path: path} 40 41 pl := txPayload{ 42 Type: tx.Type, 43 ID: tx.ID, 44 Payload: tx.Payload, 45 DeadlineMilli: tx.Deadline.UnixMilli(), 46 } 47 48 jsonBytes, err := json.Marshal(pl) 49 if err != nil { 50 return fmt.Errorf("marshal transaction payload: %w", err) 51 } 52 53 req, err := http.NewRequestWithContext(ctx, method, url.String(), 54 bytes.NewReader(jsonBytes)) 55 if err != nil { 56 return fmt.Errorf("open http request: %w", err) 57 } 58 59 req.Header.Set("content-type", "application/json") 60 61 res, err := c.client.Do(req) 62 if err != nil { 63 return fmt.Errorf("send http request: %w", err) 64 } 65 66 defer res.Body.Close() 67 body, _ := io.ReadAll(res.Body) 68 if res.StatusCode != http.StatusCreated { 69 if res.StatusCode == http.StatusConflict { 70 return cluster.ErrConcurrentTransaction 71 } 72 73 return fmt.Errorf("unexpected status code %d (%s)", res.StatusCode, 74 body) 75 } 76 77 // optional for backward-compatibility before v1.17 where only 78 // write-transactions where supported. They had no return value other than 79 // the status code. With the introduction of read-transactions it is now 80 // possible to return the requested value 81 if len(body) == 0 { 82 return nil 83 } 84 85 var txRes txResponsePayload 86 err = json.Unmarshal(body, &txRes) 87 if err != nil { 88 return fmt.Errorf("unexpected error unmarshalling tx response: %w", err) 89 } 90 91 if tx.ID != txRes.ID { 92 return fmt.Errorf("unexpected mismatch between outgoing and incoming tx ids:"+ 93 "%s vs %s", tx.ID, txRes.ID) 94 } 95 96 tx.Payload = txRes.Payload 97 98 return nil 99 } 100 101 func (c *ClusterSchema) AbortTransaction(ctx context.Context, host string, 102 tx *cluster.Transaction, 103 ) error { 104 path := "/schema/transactions/" + tx.ID 105 method := http.MethodDelete 106 url := url.URL{Scheme: "http", Host: host, Path: path} 107 108 req, err := http.NewRequestWithContext(ctx, method, url.String(), nil) 109 if err != nil { 110 return fmt.Errorf("open http request: %w", err) 111 } 112 113 res, err := c.client.Do(req) 114 if err != nil { 115 return fmt.Errorf("send http request: %w", err) 116 } 117 118 defer res.Body.Close() 119 if res.StatusCode != http.StatusNoContent { 120 errBody, _ := io.ReadAll(res.Body) 121 return fmt.Errorf("unexpected status code %d: %s", res.StatusCode, errBody) 122 } 123 124 return nil 125 } 126 127 func (c *ClusterSchema) CommitTransaction(ctx context.Context, host string, 128 tx *cluster.Transaction, 129 ) error { 130 path := "/schema/transactions/" + tx.ID + "/commit" 131 method := http.MethodPut 132 url := url.URL{Scheme: "http", Host: host, Path: path} 133 134 req, err := http.NewRequestWithContext(ctx, method, url.String(), nil) 135 if err != nil { 136 return fmt.Errorf("open http request: %w", err) 137 } 138 139 res, err := c.client.Do(req) 140 if err != nil { 141 return fmt.Errorf("send http request: %w", err) 142 } 143 144 defer res.Body.Close() 145 if res.StatusCode != http.StatusNoContent { 146 errBody, _ := io.ReadAll(res.Body) 147 return fmt.Errorf("unexpected status code %d: %s", res.StatusCode, errBody) 148 } 149 150 return nil 151 } 152 153 type txPayload struct { 154 Type cluster.TransactionType `json:"type"` 155 ID string `json:"id"` 156 Payload interface{} `json:"payload"` 157 DeadlineMilli int64 `json:"deadlineMilli"` 158 } 159 160 type txResponsePayload struct { 161 Type cluster.TransactionType `json:"type"` 162 ID string `json:"id"` 163 Payload json.RawMessage `json:"payload"` 164 }