featurebase/http_translator.go
CLoZengineer 9887a6c6d2 fix: updating code to meet linting requirements (#2171)
* removes unused filesize function

* removes ioutil usage

* updates ioutil.ReadAll to io.ReadAll

* updates ioutil.TempFile to os.CreateTemp

* updates ioutil.TempDir to os.MkdirTemp

* updates ioutil.ReadAll to os.ReadAll

* update ioutil.WriteFile to os.WriteFile

* updates ioutil.Discard to io.Discard

* updates ioutil.ReadDir to os.ReadDir where applicable

* removes unused code in idk

* creates type to use for context value keys

* replaces assert.Nil with assert.NoError for error checks
2022-10-11 11:06:31 -04:00

139 lines
3.6 KiB
Go

// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package pilosa
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"reflect"
"sync"
"github.com/featurebasedb/featurebase/v3/logger"
)
func GetOpenTranslateReaderFunc(client *http.Client) OpenTranslateReaderFunc {
return GetOpenTranslateReaderWithLockerFunc(client, nopLocker{})
}
func GetOpenTranslateReaderWithLockerFunc(client *http.Client, locker sync.Locker) OpenTranslateReaderFunc {
lockType := reflect.TypeOf(locker)
if lockType.Kind() == reflect.Ptr {
lockType = lockType.Elem()
}
return func(ctx context.Context, nodeURL string, offsets TranslateOffsetMap) (TranslateEntryReader, error) {
return openTranslateReader(ctx, nodeURL, offsets, client, reflect.New(lockType).Interface().(sync.Locker))
}
}
func openTranslateReader(ctx context.Context, nodeURL string, offsets TranslateOffsetMap, client *http.Client, locker sync.Locker) (TranslateEntryReader, error) {
r := NewTranslateEntryReader(ctx, client)
r.locker = locker
r.URL = nodeURL + "/internal/translate/data"
r.Offsets = offsets
if err := r.Open(); err != nil {
return nil, err
}
return r, nil
}
type nopLocker struct{}
func (nopLocker) Lock() {}
func (nopLocker) Unlock() {}
// TranslateEntryReader represents an implementation of TranslateEntryReader.
// It consolidates all index & field translate entries into a single reader.
type HTTPTranslateEntryReader struct {
locker sync.Locker
ctx context.Context
cancel func()
body io.ReadCloser
dec *json.Decoder
// Lookup of offsets for each index & field.
// Must be set before calling Open().
Offsets TranslateOffsetMap
// URL to stream entries from.
// Must be set before calling Open().
URL string
HTTPClient *http.Client
Logger logger.Logger
}
// NewTranslateEntryReader returns a new instance of TranslateEntryReader.
func NewTranslateEntryReader(ctx context.Context, client *http.Client) *HTTPTranslateEntryReader {
if client == nil {
client = http.DefaultClient
}
r := &HTTPTranslateEntryReader{locker: nopLocker{}, HTTPClient: client, Logger: logger.NopLogger}
r.ctx, r.cancel = context.WithCancel(ctx)
return r
}
// Open initiates the reader.
func (r *HTTPTranslateEntryReader) Open() error {
// Serialize map of offsets to request body.
requestBody, err := json.Marshal(r.Offsets)
if err != nil {
return err
}
// Connect a stream to the remote server.
req, err := http.NewRequest("POST", r.URL, bytes.NewReader(requestBody))
if err != nil {
return err
}
req = req.WithContext(r.ctx)
// Connect a stream to the remote server.
resp, err := r.HTTPClient.Do(req)
if err != nil {
return fmt.Errorf("http: cannot connect to translate store endpoint: url=%s err=%s", r.URL, err)
}
r.body = resp.Body
r.dec = json.NewDecoder(r.body)
// Handle error codes.
if resp.StatusCode == http.StatusNotImplemented {
r.body.Close()
return ErrNotImplemented
} else if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
r.body.Close()
return fmt.Errorf("http: invalid translate store endpoint status: code=%d url=%s body=%q", resp.StatusCode, r.URL, bytes.TrimSpace(body))
}
return nil
}
// Close stops the reader.
func (r *HTTPTranslateEntryReader) Close() error {
if r.cancel != nil {
r.cancel()
}
if r.body != nil {
r.locker.Lock()
err := r.body.Close()
r.locker.Unlock()
return err
}
return nil
}
// ReadEntry reads the next entry from the stream into entry.
// Returns io.EOF at the end of the stream.
func (r *HTTPTranslateEntryReader) ReadEntry(entry *TranslateEntry) error {
r.locker.Lock()
defer r.locker.Unlock()
return r.dec.Decode(&entry)
}