forked from Botanical/BotanJS
Removed old impl
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
package closure
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewCompileCache(workers int) *CompileCache {
|
||||
c := &CompileCache{
|
||||
client: NewClientFromEnv(),
|
||||
states: make(map[string]CompileState),
|
||||
results: make(map[string][]byte),
|
||||
errors: make(map[string]error),
|
||||
jobs: make(chan CompileJob, 128),
|
||||
}
|
||||
|
||||
for i := 0; i < workers; i++ {
|
||||
go c.worker()
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *CompileCache) Get(hash string) ([]byte, bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if c.states[hash] != CompileReady {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return c.results[hash], true
|
||||
}
|
||||
|
||||
func (c *CompileCache) Enqueue(job CompileJob) {
|
||||
c.mu.Lock()
|
||||
|
||||
switch c.states[job.Hash] {
|
||||
case CompilePending, CompileReady:
|
||||
c.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
c.states[job.Hash] = CompilePending
|
||||
c.mu.Unlock()
|
||||
|
||||
select {
|
||||
case c.jobs <- job:
|
||||
default:
|
||||
// Queue full. Don't block request path.
|
||||
c.mu.Lock()
|
||||
c.states[job.Hash] = CompileMissing
|
||||
c.errors[job.Hash] = errors.New("compile queue full")
|
||||
c.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CompileCache) worker() {
|
||||
for job := range c.jobs {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
|
||||
req := CompileRequest{
|
||||
ExternSources: job.ExternSources,
|
||||
JSSources: job.JSSources,
|
||||
Defines: job.Defines,
|
||||
}
|
||||
|
||||
c.client.DebugPrintCurl(ctx, req)
|
||||
|
||||
out, err := c.client.Compile(ctx, req)
|
||||
cancel()
|
||||
|
||||
c.mu.Lock()
|
||||
if err != nil {
|
||||
c.states[job.Hash] = CompileFailed
|
||||
c.errors[job.Hash] = err
|
||||
} else {
|
||||
c.states[job.Hash] = CompileReady
|
||||
c.results[job.Hash] = out
|
||||
delete(c.errors, job.Hash)
|
||||
}
|
||||
c.mu.Unlock()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package closure
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
endpoint string
|
||||
http *http.Client
|
||||
}
|
||||
|
||||
func NewClientFromEnv() *Client {
|
||||
endpoint := os.Getenv("CLOSURE_ENDPOINT")
|
||||
if endpoint == "" {
|
||||
endpoint = "http://closure-svc:8080/compile"
|
||||
}
|
||||
|
||||
return &Client{
|
||||
endpoint: endpoint,
|
||||
http: &http.Client{
|
||||
Timeout: 70 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Compile(ctx context.Context, reqBody CompileRequest) ([]byte, error) {
|
||||
body, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(
|
||||
ctx,
|
||||
http.MethodPost,
|
||||
c.endpoint,
|
||||
bytes.NewReader(body),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.http.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode/100 != 2 {
|
||||
body, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
|
||||
return nil, fmt.Errorf("closure failed: %s: %s", resp.Status, body)
|
||||
}
|
||||
|
||||
return io.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
func (c *Client) DebugPrintCurl(ctx context.Context, reqBody CompileRequest) {
|
||||
if os.Getenv("CLOSURE_DEBUG_CURL") == "" {
|
||||
return
|
||||
}
|
||||
|
||||
body, err := json.MarshalIndent(reqBody, "", " ")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "closure debug: marshal payload failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
tmpDir := os.Getenv("CLOSURE_DEBUG_DIR")
|
||||
if tmpDir == "" {
|
||||
tmpDir = os.TempDir()
|
||||
}
|
||||
|
||||
path := filepath.Join(
|
||||
tmpDir,
|
||||
fmt.Sprintf("closure-request-%d.json", time.Now().UnixNano()),
|
||||
)
|
||||
|
||||
if err := os.WriteFile(path, body, 0o600); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "closure debug: write payload failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(
|
||||
os.Stderr,
|
||||
"closure debug curl:\n curl -v -X POST %s -H 'Content-Type: application/json' --data-binary @%s\n",
|
||||
shellQuote(c.endpoint),
|
||||
shellQuote(path),
|
||||
)
|
||||
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
fmt.Fprintf(os.Stderr, "closure debug deadline: %s\n", deadline.Format(time.RFC3339Nano))
|
||||
}
|
||||
}
|
||||
|
||||
func shellQuote(s string) string {
|
||||
return strconv.Quote(s)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package closure
|
||||
|
||||
import "sync"
|
||||
|
||||
type CompileState int
|
||||
|
||||
const (
|
||||
CompileMissing CompileState = iota
|
||||
CompilePending
|
||||
CompileReady
|
||||
CompileFailed
|
||||
)
|
||||
|
||||
type CompileRequest struct {
|
||||
ExternSources []SourceInput `json:"externSources,omitempty"`
|
||||
JSSources []SourceInput `json:"jsSources"`
|
||||
Defines map[string]any `json:"defines,omitempty"`
|
||||
}
|
||||
|
||||
type SourceInput struct {
|
||||
Name string `json:"name"`
|
||||
Source string `json:"source"`
|
||||
}
|
||||
|
||||
type CompileJob struct {
|
||||
Hash string
|
||||
Mode string
|
||||
|
||||
ExternSources []SourceInput
|
||||
JSSources []SourceInput
|
||||
Defines map[string]any
|
||||
}
|
||||
|
||||
type CompileCache struct {
|
||||
client *Client
|
||||
mu sync.Mutex
|
||||
states map[string]CompileState
|
||||
results map[string][]byte
|
||||
errors map[string]error
|
||||
jobs chan CompileJob
|
||||
}
|
||||
Reference in New Issue
Block a user