Почему для l oop с goroutines приводит к отсутствующим данным - PullRequest
0 голосов
/ 02 февраля 2020

Хорошо, у меня есть два бита кода. Во-первых, это просто для l oop, который прекрасно работает

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"
    "os"

    elasticsearch "github.com/elastic/go-elasticsearch/v7"
    "github.com/elastic/go-elasticsearch/v7/esapi"
    "github.com/mitchellh/mapstructure"
)

type Esindices struct {
    Health       string `json:"health"`
    Status       string `json:"status"`
    Index        string `json:"index"`
    Uuid         string `json:"uuid"`
    Pri          string `json:"pri"`
    Rep          string `json:"rep"`
    DocsCount    string `json:"docs.count"`
    DocsDeleted  string `json:"docs.deleted"`
    StoreSize    string `json:"store.size"`
    PriStoreSize string `json:"pri.store.size"`
}

func main() {

    var r []map[string]interface{}

    es, err := elasticsearch.NewDefaultClient()
    if err != nil {
        log.Fatalf("Error creating client: %s", err)
    }

    req := esapi.CatIndicesRequest{
        Format: "json",
        Pretty: false,
    }

    res, err := req.Do(context.Background(), es)
    if err != nil {
        log.Fatalf("Error getting response: %s", err)
    }

    defer res.Body.Close()

    if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
        log.Printf("Error parsing the response body: %s", err)
    }

    indexSlice := make([]*Esindices, len(r))

    for i, element := range r {
        result := &Esindices{}
        cfg := &mapstructure.DecoderConfig{
            Metadata: nil,
            Result:   &result,
            TagName:  "json",
        }
        decoder, _ := mapstructure.NewDecoder(cfg)
        decoder.Decode(element)
        indexSlice[i] = result
    }

    thisisjson, err := json.MarshalIndent(indexSlice, "", " ")
    if err != nil {
      log.Fatal("Can't encode to JSON", err)
    }


    fmt.Fprintf(os.Stdout, "%s", thisisjson)

Большая часть этого довольно понятна, но просто чтобы уточнить, я использую клиент Elasticsearch и API api.cat.indices для получите список всех индексов в локальной установке Elasticsearch и затем сохраните их как массив map[string]interface{}, а затем l oop поверх этого, чтобы добавить их к фрагменту структуры результатов. На самом деле это нормально, но я хочу помнить о производительности, и хотя я не могу улучшить задержку самого запроса, я, безусловно, могу улучшить производительность l oop, по крайней мере, я думаю, что я должен быть в состоянии .

Поэтому, когда я пробую приведенное ниже, я получаю странные результаты.

var wg sync.WaitGroup
defer wg.Wait()
for i, element := range r {
    wg.Add(1)
    go func(i int, element map[string]interface{}) {
        defer wg.Done()
        result := Esindices{}
        cfg := &mapstructure.DecoderConfig{
            Metadata: nil,
            Result:   &result,
            TagName:  "json",
        }
        decoder, _ := mapstructure.NewDecoder(cfg)
        decoder.Decode(element)
        indexSlice[i] = result
    }(i, element)
}

Проблема, в частности, в том, что некоторые значения ключей элементов в срезе пусты , Это заставляет меня думать, что код пытается добавить к фрагменту, но он проходит, даже если это не сделано.

Мысли?

1 Ответ

2 голосов
/ 02 февраля 2020

Вместо defer wg.Wait, используйте wg.Wait в конце for-l oop. Вы используете данные, сгенерированные goroutines в for-l oop сразу после завершения for-l oop, и вы не ждете завершения всех goroutines перед тем, как использовать эти данные.

Когда вы используете defer wg.Wait, ожидание происходит в конце функции. For-l oop, использующий данные, работает с неполными данными, потому что программы все еще работают.

Когда вы используете wg.Wait в конце for-l oop, вы сначала ждете окончания всех процедур и затем используете данные, сгенерированные ими.

...