Example #1
0
func request_get_CSV(q *btrdb.Quasar, w http.ResponseWriter, r *http.Request) {
	atomic.AddInt32(&outstandingHttpReqs, 1)
	defer func() {
		atomic.AddInt32(&outstandingHttpReqs, -1)
	}()
	r.ParseForm()
	ids := r.Form.Get(":uuid")
	id := uuid.Parse(ids)
	if id == nil {
		log.Critical("ids: '%v'", ids)
		doError(w, "malformed uuid")
		return
	}
	st, ok, msg := parseInt(r.Form.Get("starttime"), -(16 << 56), (48 << 56))
	if !ok {
		doError(w, "bad start time: "+msg)
		return
	}
	et, ok, msg := parseInt(r.Form.Get("endtime"), -(16 << 56), (48 << 56))
	if !ok {
		doError(w, "bad end time: "+msg)
		return
	}
	if et <= st {
		doError(w, "end time <= start time")
		return
	}
	versions := r.Form.Get("ver")
	if versions == "" {
		versions = "0"
	}
	//Technically this is incorrect, but I doubt we will overflow this
	versioni, ok, msg := parseInt(versions, 0, 1<<63-1)
	version := uint64(versioni)
	if !ok {
		doError(w, "malformed version: "+msg)
		return
	}
	if version == 0 {
		version = btrdb.LatestGeneration
	}
	unitoftime := r.Form.Get("unitoftime")
	divisor := int64(1)
	switch unitoftime {
	case "":
		fallthrough
	case "ms":
		divisor = 1000000 //ns to ms
	case "ns":
		divisor = 1
	case "us":
		divisor = 1000 //ns to us
	case "s":
		divisor = 1000000000 //ns to s
	default:
		doError(w, "unitoftime must be 'ns', 'ms', 'us' or 's'")
		return
	}
	if st >= btrdb.MaximumTime/divisor ||
		st <= btrdb.MinimumTime/divisor {
		doError(w, "start time out of bounds")
		return
	}
	if et >= btrdb.MaximumTime/divisor ||
		et <= btrdb.MinimumTime/divisor {
		doError(w, "end time out of bounds")
		return
	}
	st *= divisor
	et *= divisor
	pws := r.Form.Get("pw")
	pw := uint8(0)
	if pws != "" {
		pwl, ok, msg := parseInt(pws, 0, 63)
		if !ok {
			doError(w, "bad point width: "+msg)
			return
		}
		if divisor != 1 {
			doError(w, "statistical results require unitoftime=ns")
			return
		}
		pw = uint8(pwl)
	}
	logh("QSVSn", fmt.Sprintf("u=%s st=%v et=%v pw=%v", id.String(), st, et, pw), r)
	rvchan, echan, _ := q.QueryStatisticalValuesStream(id, st, et, version, pw)
	w.WriteHeader(200)
	w.Write([]byte("Time[ns],Mean,Min,Max,Count\n"))
	for {
		select {
		case v, ok := <-rvchan:
			if ok {
				w.Write([]byte(fmt.Sprintf("%d,%f,%f,%f,%d\n", v.Time, v.Mean, v.Min, v.Max, v.Count)))
			} else {
				//Done
				return
			}

		case err, ok := <-echan:
			if ok {
				w.Write([]byte(fmt.Sprintf("!ABORT ERROR: %v", err)))
				return
			}
		}
	}
	return
}
Example #2
0
func (c *CPInterface) dispatchCommands(q *btrdb.Quasar, conn net.Conn) {
	//This governs the stream
	rmtx := sync.Mutex{}
	wmtx := sync.Mutex{}
	log.Info("cpnp connection")
	for !c.isShuttingDown {
		rmtx.Lock()
		seg, err := capn.ReadFromStream(conn, nil)
		if err != nil {
			log.Warning("ERR (%v) :: %v", conn.RemoteAddr(), err)
			conn.Close()
			break
		}
		rmtx.Unlock()
		go func() {
			seg := seg
			req := ReadRootRequest(seg)
			mkresp := func() (Response, *capn.Segment) {
				rvseg := capn.NewBuffer(nil)
				resp := NewRootResponse(rvseg)
				resp.SetEchoTag(req.EchoTag())
				return resp, rvseg
			}
			sendresp := func(seg *capn.Segment) {
				wmtx.Lock()
				seg.WriteTo(conn)
				wmtx.Unlock()
			}
			switch req.Which() {
			case REQUEST_QUERYSTANDARDVALUES:
				//log.Info("QSV\n")
				st := req.QueryStandardValues().StartTime()
				et := req.QueryStandardValues().EndTime()
				uuid := uuid.UUID(req.QueryStandardValues().Uuid())
				ver := req.QueryStandardValues().Version()
				//log.Info("[REQ=QsV] st=%v, et=%v, uuid=%v, gen=%v", st, et, uuid, ver)
				if ver == 0 {
					ver = btrdb.LatestGeneration
				}
				recordc, errorc, gen := q.QueryValuesStream(uuid, st, et, ver)
				if recordc == nil {
					log.Warning("RESPONDING ERR: %v", err)
					resp, rvseg := mkresp()
					resp.SetStatusCode(STATUSCODE_INTERNALERROR)
					resp.SetFinal(true)
					sendresp(rvseg)
					return
				} else {
					bufarr := make([]qtree.Record, 0, 4096)
					for {
						resp, rvseg := mkresp()
						fail := false
						fin := false
						for {
							select {
							case _, ok := <-errorc:
								if ok {
									fin = true
									fail = true
									goto donestandard
								}
							case r, ok := <-recordc:
								if !ok {
									fin = true
									goto donestandard
								}
								bufarr = append(bufarr, r)
								if len(bufarr) == cap(bufarr) {
									goto donestandard
								}
							}
						}
					donestandard:
						if fail {
							resp.SetStatusCode(STATUSCODE_INTERNALERROR)
							resp.SetFinal(true)
							//consume channels
							go func() {
								for _ = range recordc {
								}
							}()
							go func() {
								for _ = range errorc {
								}
							}()
							sendresp(rvseg)
							return
						}
						records := NewRecords(rvseg)
						rl := NewRecordList(rvseg, len(bufarr))
						rla := rl.ToArray()
						for i, v := range bufarr {
							rla[i].SetTime(v.Time)
							rla[i].SetValue(v.Val)
						}
						records.SetVersion(gen)
						records.SetValues(rl)
						resp.SetRecords(records)
						resp.SetStatusCode(STATUSCODE_OK)
						if fin {
							resp.SetFinal(true)
						}
						sendresp(rvseg)
						bufarr = bufarr[:0]
						if fin {
							return
						}
					}
				}
			case REQUEST_QUERYSTATISTICALVALUES:
				st := req.QueryStatisticalValues().StartTime()
				et := req.QueryStatisticalValues().EndTime()
				uuid := uuid.UUID(req.QueryStatisticalValues().Uuid())
				pw := req.QueryStatisticalValues().PointWidth()
				ver := req.QueryStatisticalValues().Version()
				if ver == 0 {
					ver = btrdb.LatestGeneration
				}
				recordc, errorc, gen := q.QueryStatisticalValuesStream(uuid, st, et, ver, pw)
				if recordc == nil {
					log.Warning("RESPONDING ERR: %v", err)
					resp, rvseg := mkresp()
					resp.SetStatusCode(STATUSCODE_INTERNALERROR)
					resp.SetFinal(true)
					sendresp(rvseg)
					return
				} else {
					bufarr := make([]qtree.StatRecord, 0, 4096)
					for {
						resp, rvseg := mkresp()
						fail := false
						fin := false
						for {
							select {
							case _, ok := <-errorc:
								if ok {
									fin = true
									fail = true
									goto donestat
								}
							case r, ok := <-recordc:
								if !ok {
									fin = true
									goto donestat
								}
								bufarr = append(bufarr, r)
								if len(bufarr) == cap(bufarr) {
									goto donestat
								}
							}
						}
					donestat:
						if fail {
							resp.SetStatusCode(STATUSCODE_INTERNALERROR)
							resp.SetFinal(true)
							//consume channels
							go func() {
								for _ = range recordc {
								}
							}()
							go func() {
								for _ = range errorc {
								}
							}()
							sendresp(rvseg)
							return
						}
						records := NewStatisticalRecords(rvseg)
						rl := NewStatisticalRecordList(rvseg, len(bufarr))
						rla := rl.ToArray()
						for i, v := range bufarr {
							rla[i].SetTime(v.Time)
							rla[i].SetCount(v.Count)
							rla[i].SetMin(v.Min)
							rla[i].SetMean(v.Mean)
							rla[i].SetMax(v.Max)
						}
						records.SetVersion(gen)
						records.SetValues(rl)
						resp.SetStatisticalRecords(records)
						resp.SetStatusCode(STATUSCODE_OK)
						if fin {
							resp.SetFinal(true)
						}
						sendresp(rvseg)
						bufarr = bufarr[:0]
						if fin {
							return
						}
					}
				}
			case REQUEST_QUERYVERSION:
				//ul := req.
				ul := req.QueryVersion().Uuids()
				ull := ul.ToArray()
				resp, rvseg := mkresp()
				rvers := NewVersions(rvseg)
				vlist := rvseg.NewUInt64List(len(ull))
				ulist := rvseg.NewDataList(len(ull))
				for i, v := range ull {
					ver, err := q.QueryGeneration(uuid.UUID(v))
					if err != nil {
						resp.SetStatusCode(STATUSCODE_INTERNALERROR)
						resp.SetFinal(true)
						sendresp(rvseg)
						return
					}
					//I'm not sure that the array that sits behind the uuid slice will stick around
					//so I'm copying it.
					uuid := make([]byte, 16)
					copy(uuid, v)
					vlist.Set(i, ver)
					ulist.Set(i, uuid)
				}
				resp.SetStatusCode(STATUSCODE_OK)
				rvers.SetUuids(ulist)
				rvers.SetVersions(vlist)
				resp.SetVersionList(rvers)
				resp.SetFinal(true)
				sendresp(rvseg)
			case REQUEST_QUERYNEARESTVALUE:
				resp, rvseg := mkresp()
				t := req.QueryNearestValue().Time()
				id := uuid.UUID(req.QueryNearestValue().Uuid())
				ver := req.QueryNearestValue().Version()
				if ver == 0 {
					ver = btrdb.LatestGeneration
				}
				back := req.QueryNearestValue().Backward()
				rv, gen, err := q.QueryNearestValue(id, t, back, ver)
				switch err {
				case nil:
					resp.SetStatusCode(STATUSCODE_OK)
					records := NewRecords(rvseg)
					rl := NewRecordList(rvseg, 1)
					rla := rl.ToArray()
					rla[0].SetTime(rv.Time)
					rla[0].SetValue(rv.Val)
					records.SetVersion(gen)
					records.SetValues(rl)
					resp.SetRecords(records)
				case qtree.ErrNoSuchPoint:
					resp.SetStatusCode(STATUSCODE_NOSUCHPOINT)
				default:
					resp.SetStatusCode(STATUSCODE_INTERNALERROR)
				}
				resp.SetFinal(true)
				sendresp(rvseg)
			case REQUEST_QUERYCHANGEDRANGES:
				resp, rvseg := mkresp()
				id := uuid.UUID(req.QueryChangedRanges().Uuid())
				sgen := req.QueryChangedRanges().FromGeneration()
				egen := req.QueryChangedRanges().ToGeneration()
				if egen == 0 {
					egen = btrdb.LatestGeneration
				}
				resolution := req.QueryChangedRanges().Resolution()
				rv, ver, err := q.QueryChangedRanges(id, sgen, egen, resolution)
				switch err {
				case nil:
					resp.SetStatusCode(STATUSCODE_OK)
					ranges := NewRanges(rvseg)
					ranges.SetVersion(ver)
					crl := NewChangedRangeList(rvseg, len(rv))
					crla := crl.ToArray()
					for i := 0; i < len(rv); i++ {
						crla[i].SetStartTime(rv[i].Start)
						crla[i].SetEndTime(rv[i].End)
					}
					ranges.SetValues(crl)
					resp.SetChangedRngList(ranges)
				default:
					log.Critical("qcr error: ", err)
					resp.SetStatusCode(STATUSCODE_INTERNALERROR)
				}
				resp.SetFinal(true)
				sendresp(rvseg)

			case REQUEST_INSERTVALUES:
				resp, rvseg := mkresp()
				uuid := uuid.UUID(req.InsertValues().Uuid())
				rl := req.InsertValues().Values()
				rla := rl.ToArray()
				if len(rla) != 0 {
					qtr := make([]qtree.Record, len(rla))
					for i, v := range rla {
						qtr[i] = qtree.Record{Time: v.Time(), Val: v.Value()}
					}
					q.InsertValues(uuid, qtr)
				}
				if req.InsertValues().Sync() {
					q.Flush(uuid)
				}
				resp.SetFinal(true)
				resp.SetStatusCode(STATUSCODE_OK)
				sendresp(rvseg)
			case REQUEST_DELETEVALUES:
				resp, rvseg := mkresp()
				id := uuid.UUID(req.DeleteValues().Uuid())
				stime := req.DeleteValues().StartTime()
				etime := req.DeleteValues().EndTime()
				err := q.DeleteRange(id, stime, etime)
				switch err {
				case nil:
					resp.SetStatusCode(STATUSCODE_OK)
				default:
					resp.SetStatusCode(STATUSCODE_INTERNALERROR)
				}
				resp.SetFinal(true)
				sendresp(rvseg)
			default:
				log.Critical("weird segment")
			}
		}()
	}
}
Example #3
0
func request_post_MULTICSV_IMPL(q *btrdb.Quasar, w http.ResponseWriter, bdy io.Reader, r *http.Request) {
	dec := json.NewDecoder(bdy)
	req := multi_csv_req{}
	err := dec.Decode(&req)
	if err != nil {
		doError(w, "bad request")
		return
	}
	if len(req.UUIDS) != len(req.Labels) {
		doError(w, "UUIDS and Labels must be the same length")
		return
	}
	uids := make([]uuid.UUID, len(req.UUIDS))
	for i := 0; i < len(uids); i++ {
		uids[i] = uuid.Parse(req.UUIDS[i])
		if uids[i] == nil {
			doError(w, "UUID "+string(i)+" is malformed")
			return
		}
	}
	unitoftime := req.UnitofTime
	divisor := int64(1)
	switch unitoftime {
	case "":
		fallthrough
	case "ms":
		divisor = 1000000 //ns to ms
	case "ns":
		divisor = 1
	case "us":
		divisor = 1000 //ns to us
	case "s":
		divisor = 1000000000 //ns to s
	default:
		doError(w, "unitoftime must be 'ns', 'ms', 'us' or 's'")
		return
	}
	if req.StartTime >= btrdb.MaximumTime/divisor ||
		req.StartTime <= btrdb.MinimumTime/divisor {
		doError(w, "start time out of bounds")
		return
	}
	if req.EndTime >= btrdb.MaximumTime/divisor ||
		req.EndTime <= btrdb.MinimumTime/divisor {
		doError(w, "end time out of bounds")
		return
	}
	st := req.StartTime * divisor
	et := req.EndTime * divisor
	if req.PointWidth < 0 || req.PointWidth >= 63 {
		doError(w, "PointWidth must be between 0 and 63")
		return
	}
	pw := uint8(req.PointWidth)
	chanVs := make([]chan qtree.StatRecord, len(uids))
	chanEs := make([]chan error, len(uids))
	chanBad := make([]bool, len(uids))
	chanHead := make([]qtree.StatRecord, len(uids))
	for i := 0; i < len(uids); i++ {
		logh("QSVS", fmt.Sprintf("u=%v st=%v et=%v pw=%v", uids[i].String(), st, et, pw), r)
		chanVs[i], chanEs[i], _ = q.QueryStatisticalValuesStream(uids[i], st, et, btrdb.LatestGeneration, pw)
	}
	reload := func(c int) {
		select {
		case v, ok := <-chanVs[c]:
			if ok {
				chanHead[c] = v
			} else {
				chanBad[c] = true
			}
		case e, ok := <-chanEs[c]:
			if ok {
				log.Critical("MultiCSV error: ", e)
				chanBad[c] = true
			}
		}
	}
	emit := func(r qtree.StatRecord) {
		w.Write([]byte(fmt.Sprintf(",%d,%f,%f,%f", r.Count, r.Min, r.Mean, r.Max)))
	}
	emitb := func() {
		w.Write([]byte(",,,,"))
	}
	emitt := func(t int64) {
		w.Write([]byte(fmt.Sprintf("%d", t)))
	}
	emitnl := func() {
		w.Write([]byte("\n"))
	}
	//Prime the first results
	for i := 0; i < len(uids); i++ {
		reload(i)
	}
	w.Header().Set("Content-Disposition", "attachment; filename=\"quasar_results.csv\"")
	//Print the headers
	w.Write([]byte("Time[ns]"))
	for i := 0; i < len(uids); i++ {
		w.Write([]byte(fmt.Sprintf(",%s(cnt),%s(min),%s(mean),%s(max)",
			req.Labels[i],
			req.Labels[i],
			req.Labels[i],
			req.Labels[i])))
	}
	w.Write([]byte("\n"))

	//Now merge out the results
	st = st &^ ((1 << pw) - 1)
	for t := st; t < et; t += (1 << pw) {
		//First locate the min time
		minset := false
		min := int64(0)
		for i := 0; i < len(uids); i++ {
			for !chanBad[i] && chanHead[i].Time < t {
				log.Warning("discarding duplicate time %v:%v", i, chanHead[i].Time)
				reload(i)
			}
			if !chanBad[i] && (!minset || chanHead[i].Time < min) {
				minset = true
				min = chanHead[i].Time
			}
		}
		if minset == false {
			//We are done. There are no more live streams
			return
		}
		//If the min time is later than t, emit blank lines until we catch up
		for ; t < min; t += (1 << pw) {
			emitt(t)
			emitnl()
		}
		if t != min {
			log.Critical("WTF t=%v, min=%v, pw=%v, dt=%v, dm=%v delte=%v",
				t, min, 1<<pw, t&((1<<pw)-1), min&((1<<pw)-1), min-t)
			log.Panic("wellfuck")
		}
		//Now emit all values at that time
		emitt(t)
		for i := 0; i < len(uids); i++ {
			if !chanBad[i] && chanHead[i].Time == min {
				emit(chanHead[i])
				reload(i)
			} else {
				emitb()
			}
		}
		emitnl()

	}

}