Exemple #1
0
func (m *BoltBackend) flushAddBuffer() {
	// Insert it in a database transaction
	err := m.db.Update(func(tx *bolt.Tx) error {
		for seriesName, seriesData := range m.addBuffer {
			b, err := tx.CreateBucketIfNotExists([]byte(seriesName))
			if err != nil {
				return err
			}

			for _, m := range seriesData {
				key := make([]byte, 5)
				metrics.TimeToUint40(key, m.Time)

				value := putFloat64(m.Value)

				// Write data
				err = b.Put(key, value)
				if err != nil {
					return err
				}
			}

			// Remove data from buffer
			delete(m.addBuffer, seriesName)
		}
		return nil
	})

	m.addBuffer = make(map[string][]metrics.Metric)
	m.addBufferSize = 0

	if err != nil {
		fmt.Errorf("Bolt was unhappy writing data: %v", err)
	}

}
Exemple #2
0
func (m *BoltBackend) Start() {
	go func() {
		t := time.NewTicker(time.Second * 10)
		for {
			select {
			case <-t.C:
				// Regularly flush the internal buffer
				m.flushAddBuffer()

			case metric := <-m.addChan:
				// Add it to the internal buffer
				m.addBuffer[metric.Name] = append(m.addBuffer[metric.Name], metric)
				m.addBufferSize += 1

				// Break if our buffer is too small
				if m.addBufferSize < 10000 {
					break
				}

				m.flushAddBuffer()

			case req := <-m.listRequests:
				// List all buckets in a view-transaction
				m.db.View(func(tx *bolt.Tx) error {
					c := tx.Cursor()
					k, _ := c.First()
					for k != nil {
						// Skip sending this metric if it's in the buffer
						if _, ok := m.addBuffer[string(k)]; !ok {
							req <- string(k)
						}
						k, _ = c.Next()
					}
					return nil
				})

				// Add whatever we find in the buffer
				for name := range m.addBuffer {
					req <- name
				}

				close(req)

			case req := <-m.dataRequests:
				// List all relevant data in a view-transaction
				m.db.View(func(tx *bolt.Tx) error {
					b := tx.Bucket([]byte(req.Name))

					// Bail if bucket does not exist
					if b == nil {
						fmt.Printf("Bolt - no series '%v'.\n", req.Name)
						return nil
					}

					// It's kosher - return relevant data
					cursor := b.Cursor()

					// Generate the first key, seek to it and begin reading data
					firstKey := make([]byte, 5)
					metrics.TimeToUint40(firstKey, req.From)
					rawTime, rawVal := cursor.Seek(firstKey)
					metricTime := metrics.Uint40ToTime(rawTime)

					// Loop over the rest
					for metricTime.Before(req.To) {
						// Send the data
						req.Result <- metrics.MetricValue{
							Time:  metricTime,
							Value: parseFloat64(rawVal),
						}

						// Extract a new time/value
						rawTime, rawVal = cursor.Next()

						// Break from the loop if we reach the end
						if rawTime == nil {
							break
						}

						// Parse time and loop-de-loop
						metricTime = metrics.Uint40ToTime(rawTime)
					}

					return nil
				})

				// Add stuff from the buffer
				if data, ok := m.addBuffer[req.Name]; ok {
					for _, m := range data {
						if m.Time.After(req.From) && m.Time.Before(req.To) {
							req.Result <- metrics.MetricValue{
								Time:  m.Time,
								Value: m.Value,
							}
						}
					}
				}

				close(req.Result)

			case <-m.stopChan:
				// Stop comm chans and shut down database
				t.Stop()
				close(m.stopChan)
				close(m.addChan)
				close(m.listRequests)
				close(m.dataRequests)
				m.db.Close()
				return
			}
		}
	}()
}