mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-09-06 08:35:55 +00:00
- had to make sure we don't snapshot until directive is fully applied on a computer... otherwise there's races between loading the files and truncating the write log. - added a dirty bit to resources and a bool return to incrementing the write log... don't snapshot if it returns false because that means there's been no writes. (but make sure you close the storage transaction!) - added the actually snapshotting routine which just fires every <timeout> and serially snapshots everything. - tweaked some logging - added ability to get all tables in an org/db or literally all. I think I just needed the "literally all", but it was natural to allow it to be scoped to org or DB as well.
188 lines
5.3 KiB
Go
188 lines
5.3 KiB
Go
// Package http provides the http implementation of the Director interface.
|
|
package http
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/molecula/featurebase/v3/dax"
|
|
"github.com/molecula/featurebase/v3/dax/mds/controller"
|
|
"github.com/molecula/featurebase/v3/errors"
|
|
"github.com/molecula/featurebase/v3/logger"
|
|
)
|
|
|
|
// Ensure type implements interface.
|
|
var _ controller.Director = (*Director)(nil)
|
|
|
|
// Director is an http implementation of the Director interface.
|
|
type Director struct {
|
|
// directivePath is the path portion of the URI to which directives should
|
|
// be POSTed.
|
|
directivePath string
|
|
|
|
// snapshotRequestPath is the path portion of the URI to which snapshot
|
|
// requests should be POSTed.
|
|
snapshotRequestPath string
|
|
|
|
client *http.Client
|
|
|
|
logger logger.Logger
|
|
}
|
|
|
|
func NewDirector(cfg DirectorConfig) *Director {
|
|
var logr = logger.NopLogger
|
|
if cfg.Logger != nil {
|
|
logr = cfg.Logger
|
|
}
|
|
|
|
return &Director{
|
|
directivePath: cfg.DirectivePath,
|
|
snapshotRequestPath: cfg.SnapshotRequestPath,
|
|
logger: logr,
|
|
client: &http.Client{
|
|
Transport: &http.Transport{
|
|
Proxy: http.ProxyFromEnvironment,
|
|
DialContext: (&net.Dialer{
|
|
Timeout: 2 * time.Second,
|
|
KeepAlive: 30 * time.Second,
|
|
}).DialContext,
|
|
ForceAttemptHTTP2: true,
|
|
MaxIdleConns: 100,
|
|
IdleConnTimeout: 90 * time.Second,
|
|
TLSHandshakeTimeout: 3 * time.Second,
|
|
ExpectContinueTimeout: 1 * time.Second,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
type DirectorConfig struct {
|
|
DirectivePath string
|
|
SnapshotRequestPath string
|
|
Logger logger.Logger
|
|
}
|
|
|
|
func (d *Director) SendDirective(ctx context.Context, dir *dax.Directive) error {
|
|
url := fmt.Sprintf("%s/%s", dir.Address.WithScheme("http"), d.directivePath)
|
|
d.logger.Printf("SEND HTTP directive to: %s\n", url)
|
|
|
|
// Encode the request.
|
|
postBody, err := json.Marshal(dir)
|
|
if err != nil {
|
|
return errors.Wrap(err, "marshalling directive to json")
|
|
}
|
|
requestBody := bytes.NewBuffer(postBody)
|
|
|
|
// Post the request.
|
|
request, _ := http.NewRequest(http.MethodPost, url, requestBody)
|
|
request.Header.Add("Content-Type", "application/json")
|
|
request.Header.Add("Accept", "application/json")
|
|
|
|
resp, err := d.client.Do(request)
|
|
if err != nil {
|
|
return errors.Wrap(err, "doing send directive")
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
b, _ := io.ReadAll(resp.Body)
|
|
return errors.Errorf("status code: %d: %s", resp.StatusCode, b)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *Director) SendSnapshotShardDataRequest(ctx context.Context, req *dax.SnapshotShardDataRequest) error {
|
|
url := fmt.Sprintf("%s/%s/shard-data", req.Address.WithScheme("http"), d.snapshotRequestPath)
|
|
|
|
// Encode the request.
|
|
postBody, err := json.Marshal(req)
|
|
if err != nil {
|
|
return errors.Wrap(err, "marshalling snapshot shard data request to json")
|
|
}
|
|
requestBody := bytes.NewBuffer(postBody)
|
|
|
|
// Post the request.
|
|
request, _ := http.NewRequest(http.MethodPost, url, requestBody)
|
|
request.Header.Add("Content-Type", "application/json")
|
|
request.Header.Add("Accept", "application/json")
|
|
|
|
resp, err := d.client.Do(request)
|
|
if err != nil {
|
|
return errors.Wrap(err, "doing snapshot shard data request")
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
b, _ := io.ReadAll(resp.Body)
|
|
return errors.Errorf("status code: %d: %s", resp.StatusCode, b)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *Director) SendSnapshotTableKeysRequest(ctx context.Context, req *dax.SnapshotTableKeysRequest) error {
|
|
url := fmt.Sprintf("%s/%s/table-keys", req.Address.WithScheme("http"), d.snapshotRequestPath)
|
|
d.logger.Printf("SEND HTTP snapshot table keys request to: %s\n", url)
|
|
|
|
// Encode the request.
|
|
postBody, err := json.Marshal(req)
|
|
if err != nil {
|
|
return errors.Wrap(err, "marshalling snapshot table keys request to json")
|
|
}
|
|
requestBody := bytes.NewBuffer(postBody)
|
|
|
|
// Post the request.
|
|
request, _ := http.NewRequest(http.MethodPost, url, requestBody)
|
|
request.Header.Add("Content-Type", "application/json")
|
|
request.Header.Add("Accept", "application/json")
|
|
|
|
resp, err := d.client.Do(request)
|
|
if err != nil {
|
|
return errors.Wrap(err, "doing snapshot table keys request")
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
b, _ := io.ReadAll(resp.Body)
|
|
return errors.Errorf("status code: %d: %s", resp.StatusCode, b)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *Director) SendSnapshotFieldKeysRequest(ctx context.Context, req *dax.SnapshotFieldKeysRequest) error {
|
|
url := fmt.Sprintf("%s/%s/field-keys", req.Address.WithScheme("http"), d.snapshotRequestPath)
|
|
d.logger.Printf("SEND HTTP snapshot field keys request to: %s\n", url)
|
|
|
|
// Encode the request.
|
|
postBody, err := json.Marshal(req)
|
|
if err != nil {
|
|
return errors.Wrap(err, "marshalling snapshot field keys request to json")
|
|
}
|
|
requestBody := bytes.NewBuffer(postBody)
|
|
|
|
// Post the request.
|
|
request, _ := http.NewRequest(http.MethodPost, url, requestBody)
|
|
request.Header.Add("Content-Type", "application/json")
|
|
request.Header.Add("Accept", "application/json")
|
|
|
|
resp, err := d.client.Do(request)
|
|
if err != nil {
|
|
return errors.Wrap(err, "doing snapshot field keys request")
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
b, _ := io.ReadAll(resp.Body)
|
|
return errors.Errorf("status code: %d: %s", resp.StatusCode, b)
|
|
}
|
|
|
|
return nil
|
|
}
|