Go 1.14では、上記のようなコードでも切り替えられるように、ゴルーチンの切り替えがSA_RESTARTフラグ付きのSIGURGによっても行われるようになりました。ざっとコードを眺めた雰囲気だと、SIGURGはGCの処理で送っているみたいですね。この結果、Go 1.14のリリースノートには
A consequence of the implementation of preemption is that on Unix systems, including Linux and macOS systems, programs built with Go 1.14 will receive more signals than programs built with earlier releases. This means that programs that use packages like syscall or golang.org/x/sys/unix will see more slow system calls fail with EINTR errors.
type temporaryer interface {
Temporary() bool
}
_, err := r.Read(buf)
if err != nil {
if e, ok := err.(temporaryer); ok {
fmt.Println(e.Temporary())
}
}
Go 1.13時点では、モジュール管理しているリポジトリでgoimportsなどのツールをgo getすると、go.modが書き換えられて管理対象に入ります*1が、恒久的にソースコードへ含まれる訳ではないため、go mod tidyなどで整理すると、ツールのインストール時に追加されたモジュールがgo.modから削除されます。ここではツールもバージョン管理したいので、ビルド制約(build constraints)でビルド対象に含まれないようにしたファイルに、利用するツールを書き並べていきます。このときのビルドタグやファイル名はなんでも構いませんが、公式ではファイル名にtools.go、ビルドタグにtoolsが使われているので、合わせておいくといいでしょう。
// バージョン確認
% go list -m-u all
// 以下のうちどれかでアップデート
% go get golang.org/x/lint/golint
% go get -u golang.org/x/lint/golint
% go get -u=patch golang.org/x/lint/golint
% git add go.mod go.sum
go getの使い分け
Goモジュールではgo getだけでgo.modに関わらず最新のバージョンを取得するので、go get -uとの違いについてGOPATHモードのgo getを知っている人は混乱するかもしれません。これはgo help module-getによると、インストールするモジュールが依存するモジュールをどう扱うか、を表すようです。
go get <pkg>: <pkg>のgo.modに書かれたバージョンをminimal version selectionで維持する
go get -u <pkg>: <pkg>が依存するモジュールも同じメジャーバージョン内でアップデートする
go get -u=patch <pkg>: <pkg>が依存するモジュールも同じマイナーバージョン内でアップデートする
% go version
go version go1.13.5 darwin/amd64
% go get github.com/github/hub@v2.14.1
go: finding github.com/github/hub v2.14.1
go: finding github.com/github/hub v2.14.1
go get github.com/github/hub@v2.14.1: github.com/github/hub@v2.14.1: invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v2
for _, a := range requests { // 500件ごとに分割してある
b := c.Batch()
for _, p := range a {
b.Create(c.Doc(p.Key), p)
}
if err := b.Commit(); err != nil {
return err
}
}
e, err := exporter.NewExporter(exporter.Options{
})
if err != nil {
log.Fatal(err)
}
if err := e.Start(1 * time.Minute); err != nil {
log.Fatal(err)
}
defer e.Stop()
Exporter自体の実装。
// Exporter is a stats exporter that uploads data to Mackerel.type Exporter struct {
opts Options
once sync.Once
r *metricexport.IntervalReader
c *mackerel.Client
}
// Options contains options for configuring the exporter.type Options struct {
APIKey string
}
func NewExporter(o Options) (*Exporter, error) {
c := mackerel.NewClient(o.APIKey)
return &Exporter{
opts: o,
c: c,
}, nil
}
// Start starts the metric exporter.func (e *Exporter) Start(interval time.Duration) error {
var err error
e.once.Do(func() {
e.r, err = metricexport.NewIntervalReader(&metricexport.Reader{}, e)
})
if err != nil {
return err
}
//trace.RegisterExporter(e)
e.r.ReportingInterval = interval
return e.r.Start()
}
func (e *Exporter) Stop() {
//trace.UnregisterExporter(e)
e.r.Stop()
}
func (e *Exporter) ExportMetrics(ctx context.Context, data []*metricdata.Metric) error {
a := convertToHostMetrics(data)
if err := e.c.PostHostMetricValues(a); err != nil {
e.ErrLog(err)
return err
}
returnnil
}
func convertToHostMetrics(a []*metricdata.Metric) []*mackerel.HostMetricValue {
var r []*mackerel.HostMetricValue
for _, p := range a {
// View.Nameの値から'/'を'.'に置き換え
name := metricName(p.Descriptor)
// 値と一緒に記録したタグからホストIDを取り出す
i := labelKeyIndex(p.Descriptor, HostKeyID.Name())
if i < 0 {
continue
}
for _, ts := range p.TimeSeries {
if !ts.LabelValues[i].Present {
continue
}
hostID := ts.LabelValues[i].Value
// OpenCensusのMetricをMackerelのホストメトリックに変換
a := hostMetricValues(hostID, metricValues(name, ts.Points))
r = append(r, a...)
}
}
return r
}
func labelKeyIndex(d metricdata.Descriptor, key string) int {
for i, k := range d.LabelKeys {
if k.Key == key {
return i
}
}
return -1
}
func hostMetricValues(...省略...)