| 
									
										
										
										
											2021-01-30 19:04:48 +08:00
										 |  |  | // Copyright 2021 The Prometheus Authors
 | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License");
 | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License.
 | 
					
						
							|  |  |  | // You may obtain a copy of the License at
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software
 | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS,
 | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and
 | 
					
						
							|  |  |  | // limitations under the License.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package remote | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2022-07-02 00:59:50 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2021-09-22 04:53:27 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2021-01-30 19:04:48 +08:00
										 |  |  | 	"net/http" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-12 00:17:59 +08:00
										 |  |  | 	"github.com/go-kit/log" | 
					
						
							|  |  |  | 	"github.com/go-kit/log/level" | 
					
						
							| 
									
										
										
										
											2021-09-22 04:53:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-08 22:23:17 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/model/exemplar" | 
					
						
							| 
									
										
										
										
											2021-01-30 19:04:48 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/prompb" | 
					
						
							|  |  |  | 	"github.com/prometheus/prometheus/storage" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-27 00:43:19 +08:00
										 |  |  | type writeHandler struct { | 
					
						
							| 
									
										
										
										
											2021-01-30 19:04:48 +08:00
										 |  |  | 	logger     log.Logger | 
					
						
							|  |  |  | 	appendable storage.Appendable | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewWriteHandler creates a http.Handler that accepts remote write requests and
 | 
					
						
							|  |  |  | // writes them to the provided appendable.
 | 
					
						
							|  |  |  | func NewWriteHandler(logger log.Logger, appendable storage.Appendable) http.Handler { | 
					
						
							| 
									
										
										
										
											2021-02-27 00:43:19 +08:00
										 |  |  | 	return &writeHandler{ | 
					
						
							| 
									
										
										
										
											2021-01-30 19:04:48 +08:00
										 |  |  | 		logger:     logger, | 
					
						
							|  |  |  | 		appendable: appendable, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-27 00:43:19 +08:00
										 |  |  | func (h *writeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2021-01-30 19:04:48 +08:00
										 |  |  | 	req, err := DecodeWriteRequest(r.Body) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		level.Error(h.logger).Log("msg", "Error decoding remote write request", "err", err.Error()) | 
					
						
							|  |  |  | 		http.Error(w, err.Error(), http.StatusBadRequest) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = h.write(r.Context(), req) | 
					
						
							|  |  |  | 	switch err { | 
					
						
							|  |  |  | 	case nil: | 
					
						
							|  |  |  | 	case storage.ErrOutOfOrderSample, storage.ErrOutOfBounds, storage.ErrDuplicateSampleForTimestamp: | 
					
						
							|  |  |  | 		// Indicated an out of order sample is a bad request to prevent retries.
 | 
					
						
							|  |  |  | 		http.Error(w, err.Error(), http.StatusBadRequest) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		level.Error(h.logger).Log("msg", "Error appending remote write", "err", err.Error()) | 
					
						
							|  |  |  | 		http.Error(w, err.Error(), http.StatusInternalServerError) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.WriteHeader(http.StatusNoContent) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 04:53:27 +08:00
										 |  |  | // checkAppendExemplarError modifies the AppendExamplar's returned error based on the error cause.
 | 
					
						
							|  |  |  | func (h *writeHandler) checkAppendExemplarError(err error, e exemplar.Exemplar, outOfOrderErrs *int) error { | 
					
						
							| 
									
										
										
										
											2022-07-02 00:59:50 +08:00
										 |  |  | 	unwrapedErr := errors.Unwrap(err) | 
					
						
							|  |  |  | 	switch { | 
					
						
							|  |  |  | 	case errors.Is(unwrapedErr, storage.ErrNotFound): | 
					
						
							| 
									
										
										
										
											2021-09-22 04:53:27 +08:00
										 |  |  | 		return storage.ErrNotFound | 
					
						
							| 
									
										
										
										
											2022-07-02 00:59:50 +08:00
										 |  |  | 	case errors.Is(unwrapedErr, storage.ErrOutOfOrderExemplar): | 
					
						
							| 
									
										
										
										
											2021-09-22 04:53:27 +08:00
										 |  |  | 		*outOfOrderErrs++ | 
					
						
							|  |  |  | 		level.Debug(h.logger).Log("msg", "Out of order exemplar", "exemplar", fmt.Sprintf("%+v", e)) | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-27 00:43:19 +08:00
										 |  |  | func (h *writeHandler) write(ctx context.Context, req *prompb.WriteRequest) (err error) { | 
					
						
							| 
									
										
										
										
											2021-10-22 16:06:44 +08:00
										 |  |  | 	outOfOrderExemplarErrs := 0 | 
					
						
							| 
									
										
										
										
											2021-09-22 04:53:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-30 19:04:48 +08:00
										 |  |  | 	app := h.appendable.Appender(ctx) | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-09-22 04:53:27 +08:00
										 |  |  | 			_ = app.Rollback() | 
					
						
							| 
									
										
										
										
											2021-01-30 19:04:48 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		err = app.Commit() | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 04:53:27 +08:00
										 |  |  | 	var exemplarErr error | 
					
						
							| 
									
										
										
										
											2021-01-30 19:04:48 +08:00
										 |  |  | 	for _, ts := range req.Timeseries { | 
					
						
							|  |  |  | 		labels := labelProtosToLabels(ts.Labels) | 
					
						
							|  |  |  | 		for _, s := range ts.Samples { | 
					
						
							| 
									
										
										
										
											2021-02-18 20:07:00 +08:00
										 |  |  | 			_, err = app.Append(0, labels, s.Timestamp, s.Value) | 
					
						
							| 
									
										
										
										
											2021-01-30 19:04:48 +08:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-07-02 00:59:50 +08:00
										 |  |  | 				unwrapedErr := errors.Unwrap(err) | 
					
						
							|  |  |  | 				if errors.Is(unwrapedErr, storage.ErrOutOfOrderSample) || errors.Is(unwrapedErr, storage.ErrOutOfBounds) || errors.Is(unwrapedErr, storage.ErrDuplicateSampleForTimestamp) { | 
					
						
							| 
									
										
										
										
											2021-12-08 23:07:51 +08:00
										 |  |  | 					level.Error(h.logger).Log("msg", "Out of order sample from remote write", "err", err.Error(), "series", labels.String(), "timestamp", s.Timestamp) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2021-01-30 19:04:48 +08:00
										 |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-09-22 04:53:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-30 19:04:48 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-09-22 04:53:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		for _, ep := range ts.Exemplars { | 
					
						
							|  |  |  | 			e := exemplarProtoToExemplar(ep) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			_, exemplarErr = app.AppendExemplar(0, labels, e) | 
					
						
							|  |  |  | 			exemplarErr = h.checkAppendExemplarError(exemplarErr, e, &outOfOrderExemplarErrs) | 
					
						
							|  |  |  | 			if exemplarErr != nil { | 
					
						
							|  |  |  | 				// Since exemplar storage is still experimental, we don't fail the request on ingestion errors.
 | 
					
						
							|  |  |  | 				level.Debug(h.logger).Log("msg", "Error while adding exemplar in AddExemplar", "exemplar", fmt.Sprintf("%+v", e), "err", exemplarErr) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if outOfOrderExemplarErrs > 0 { | 
					
						
							|  |  |  | 		_ = level.Warn(h.logger).Log("msg", "Error on ingesting out-of-order exemplars", "num_dropped", outOfOrderExemplarErrs) | 
					
						
							| 
									
										
										
										
											2021-01-30 19:04:48 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } |