func (s *Service) Diagnostics() (*diagnostics.Diagnostics, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	d := &diagnostics.Diagnostics{
		Columns: []string{"node", "active", "last modified", "head", "tail"},
		Rows:    make([][]interface{}, 0, len(s.processors)),
	}
	for k, v := range s.processors {
		lm, err := v.LastModified()
		if err != nil {
			return nil, err
		}
		active, err := v.Active()
		if err != nil {
			return nil, err
		}
		d.Rows = append(d.Rows, []interface{}{k, active, lm, v.Head(), v.Tail()})
	}
	return d, nil
}
Diagnostics returns diagnostic information.
package hh  
import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"sync"
	"time"
	"github.com/influxdata/influxdb/models"
	"github.com/influxdata/influxdb/services/meta"
	"github.com/influxdata/influxdb/monitor/diagnostics"
	"sync/atomic"
)
var ErrHintedHandoffDisabled = fmt.Errorf("hinted handoff disabled")
const (
	writeShardReq       = "writeShardReq"
	writeShardReqPoints = "writeShardReqPoints"
)
type Service struct {
	mu      sync.RWMutex
	wg      sync.WaitGroup
	closing chan struct{}
	processors map[uint64]*NodeProcessor
	Logger *log.Logger
	cfg    Config
	shardWriter shardWriter
	MetaClient  metaClient
	Monitor interface {
		RegisterDiagnosticsClient(name string, client diagnostics.Client)
		DeregisterDiagnosticsClient(name string)
	}
	stats *HHStatistics
}
type shardWriter interface {
	WriteShard(shardID, ownerID uint64, points []models.Point) error
}
type metaClient interface {
	DataNode(id uint64) (ni *meta.NodeInfo, err error)
}
func NewService(c Config, w shardWriter, m metaClient) *Service {
	return &Service{
		cfg:         c,
		closing:     make(chan struct{}),
		processors:  make(map[uint64]*NodeProcessor),
		Logger:      log.New(os.Stderr, "[handoff] ", log.LstdFlags),
		shardWriter: w,
		MetaClient:  m,
		stats:       &HHStatistics{},
	}
}
func (s *Service) Open() error {
	s.mu.Lock()
	defer s.mu.Unlock()
	if !s.cfg.Enabled {
		return nil
	}
	s.Logger.Printf("Starting hinted handoff service")
	s.closing = make(chan struct{})
	if s.Monitor != nil {
		s.Monitor.RegisterDiagnosticsClient("hh", s)
	}
	s.Logger.Printf("Using data dir: %v", s.cfg.Dir)
	if err := os.MkdirAll(s.cfg.Dir, 0700); err != nil {
		return fmt.Errorf("mkdir all: %s", err)
	}
	files, err := ioutil.ReadDir(s.cfg.Dir)
	if err != nil {
		return err
	}
	for _, file := range files {
		nodeID, err := strconv.ParseUint(file.Name(), 10, 64)
		if err != nil {
			continue
		}
		n := NewNodeProcessor(nodeID, s.pathforNode(nodeID), s.shardWriter, s.MetaClient)
		if err := n.Open(); err != nil {
			return err
		}
		s.processors[nodeID] = n
	}
	s.wg.Add(1)
	go s.purgeInactiveProcessors()
	return nil
}
func (s *Service) Close() error {
	s.Logger.Println("shutting down hh service")
	s.mu.Lock()
	defer s.mu.Unlock()
	for _, p := range s.processors {
		if err := p.Close(); err != nil {
			return err
		}
	}
	if s.Monitor != nil {
		s.Monitor.DeregisterDiagnosticsClient("hh")
	}
	if s.closing != nil {
		close(s.closing)
	}
	s.wg.Wait()
	s.closing = nil
	return nil
}
type HHStatistics struct {
	WriteShardReq       int64
	WriteShardReqPoints int64
}
func (s *Service) Statistics(tags map[string]string) []models.Statistic {
	statistics := []models.Statistic{{
		Name: "hinted_handoff",
		Tags: tags,
		Values: map[string]interface{}{
			writeShardReq:       atomic.LoadInt64(&s.stats.WriteShardReq),
			writeShardReqPoints: atomic.LoadInt64(&s.stats.WriteShardReqPoints),
		},
	}}
	for _, p := range s.processors {
		statistics = append(statistics, p.Statistics(tags)...)
	}
	return statistics
}
func (s *Service) SetLogger(l *log.Logger) {
	s.Logger = l
}
func (s *Service) WriteShard(shardID, ownerID uint64, points []models.Point) error {
	if !s.cfg.Enabled {
		return ErrHintedHandoffDisabled
	}
	atomic.AddInt64(&s.stats.WriteShardReq, 1)
	atomic.AddInt64(&s.stats.WriteShardReqPoints, int64(len(points)))
	s.mu.RLock()
	processor, ok := s.processors[ownerID]
	s.mu.RUnlock()
	if !ok {
		if err := func() error {
			s.mu.Lock()
			defer s.mu.Unlock()
			processor, ok = s.processors[ownerID]
			if !ok {
				processor = NewNodeProcessor(ownerID, s.pathforNode(ownerID), s.shardWriter, s.MetaClient)
				if err := processor.Open(); err != nil {
					return err
				}
				s.processors[ownerID] = processor
			}
			return nil
		}(); err != nil {
			return err
		}
	}
	if err := processor.WriteShard(shardID, points); err != nil {
		return err
	}
	return nil
}