github.com/weaviate/weaviate@v1.24.6/test/acceptance/stress_tests/stress.go (about)

     1  //                           _       _
     2  // __      _____  __ ___   ___  __ _| |_ ___
     3  // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
     4  //  \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
     5  //   \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
     6  //
     7  //  Copyright © 2016 - 2024 Weaviate B.V. All rights reserved.
     8  //
     9  //  CONTACT: hello@weaviate.io
    10  //
    11  
    12  package stress_tests
    13  
    14  import (
    15  	"bytes"
    16  	"encoding/json"
    17  	"fmt"
    18  	"io"
    19  	"net"
    20  	"net/http"
    21  	"strings"
    22  	"time"
    23  
    24  	"github.com/go-openapi/strfmt"
    25  	"github.com/google/uuid"
    26  	"github.com/pkg/errors"
    27  	"github.com/weaviate/weaviate/entities/models"
    28  	"github.com/weaviate/weaviate/entities/schema"
    29  )
    30  
    31  // If there is already a schema present, clear it out
    32  func clearExistingObjects(c *http.Client, url string) {
    33  	checkSchemaRequest := createRequest(url+"schema", "GET", nil)
    34  	checkSchemaResponseCode, body, err := performRequest(c, checkSchemaRequest)
    35  	if err != nil {
    36  		panic(errors.Wrap(err, "perform request"))
    37  	}
    38  	if checkSchemaResponseCode != 200 {
    39  		return
    40  	}
    41  
    42  	var dump models.Schema
    43  	if err := json.Unmarshal(body, &dump); err != nil {
    44  		panic(errors.Wrap(err, "Could not unmarshal read response"))
    45  	}
    46  	for _, classObj := range dump.Classes {
    47  		requestDelete := createRequest(url+"schema/"+classObj.Class, "DELETE", nil)
    48  		responseDeleteCode, _, err := performRequest(c, requestDelete)
    49  		if err != nil {
    50  			panic(errors.Wrap(err, "Could delete schema"))
    51  		}
    52  		if responseDeleteCode != 200 {
    53  			panic(fmt.Sprintf("Could not delete schema, code: %v", responseDeleteCode))
    54  		}
    55  	}
    56  }
    57  
    58  func createHttpClient() *http.Client {
    59  	httpT := &http.Transport{
    60  		Proxy: http.ProxyFromEnvironment,
    61  		DialContext: (&net.Dialer{
    62  			Timeout:   500 * time.Second,
    63  			KeepAlive: 120 * time.Second,
    64  		}).DialContext,
    65  		MaxIdleConnsPerHost:   100,
    66  		MaxIdleConns:          100,
    67  		IdleConnTimeout:       90 * time.Second,
    68  		TLSHandshakeTimeout:   10 * time.Second,
    69  		ExpectContinueTimeout: 1 * time.Second,
    70  	}
    71  	return &http.Client{Transport: httpT}
    72  }
    73  
    74  // createRequest creates requests
    75  func createRequest(url string, method string, payload interface{}) *http.Request {
    76  	var body io.Reader = nil
    77  	if payload != nil {
    78  		jsonBody, err := json.Marshal(payload)
    79  		if err != nil {
    80  			panic(errors.Wrap(err, "Could not marshal request"))
    81  		}
    82  		body = bytes.NewBuffer(jsonBody)
    83  	}
    84  	request, err := http.NewRequest(method, url, body)
    85  	if err != nil {
    86  		panic(errors.Wrap(err, "Could not create request"))
    87  	}
    88  	request.Header.Add("Content-Type", "application/json")
    89  	request.Header.Add("Accept", "application/json")
    90  
    91  	return request
    92  }
    93  
    94  // performRequest runs requests
    95  func performRequest(c *http.Client, request *http.Request) (int, []byte, error) {
    96  	for {
    97  		response, err := c.Do(request)
    98  		if err != nil {
    99  			return 0, nil, err
   100  		}
   101  
   102  		body, err := io.ReadAll(response.Body)
   103  		response.Body.Close()
   104  		if err != nil {
   105  			return 0, nil, err
   106  		}
   107  
   108  		if response.StatusCode == 200 {
   109  			return response.StatusCode, body, nil
   110  		}
   111  		time.Sleep(time.Millisecond * 10)
   112  		var result map[string]interface{}
   113  		json.Unmarshal(body, &result)
   114  		message := result["error"].([]interface{})[0].(map[string]interface{})["message"].(string)
   115  
   116  		if strings.Contains(message, "concurrent transaction") {
   117  			time.Sleep(time.Millisecond * 10)
   118  			continue
   119  		}
   120  		return response.StatusCode, body, nil
   121  	}
   122  }
   123  
   124  func createSchemaRequest(url string, class string, multiTenantcy bool) *http.Request {
   125  	classObj := &models.Class{
   126  		Class:       class,
   127  		Description: "Dummy class for benchmarking purposes",
   128  		MultiTenancyConfig: &models.MultiTenancyConfig{
   129  			Enabled: multiTenantcy,
   130  		},
   131  		Properties: []*models.Property{
   132  			{
   133  				DataType:    []string{"int"},
   134  				Description: "The value of the counter in the dataset",
   135  				Name:        "counter",
   136  			},
   137  			{
   138  				DataType:     schema.DataTypeText.PropString(),
   139  				Tokenization: models.PropertyTokenizationWhitespace,
   140  				Description:  "The value of the counter in the dataset",
   141  				Name:         "name",
   142  			},
   143  		},
   144  	}
   145  	request := createRequest(url+"schema", "POST", classObj)
   146  	return request
   147  }
   148  
   149  func createObject(class string) []*models.Object {
   150  	objects := []*models.Object{
   151  		{
   152  			Class:  class,
   153  			ID:     strfmt.UUID(uuid.New().String()),
   154  			Vector: models.C11yVector([]float32{1.0, 2, 534, 324, 0.0001}),
   155  			Properties: map[string]interface{}{
   156  				"counter":   50,
   157  				"counter2":  45,
   158  				"something": "JustSlammedMyKeyboardahudghoig",
   159  			},
   160  		},
   161  	}
   162  	return objects
   163  }
   164  
   165  func createBatch(class string, batchSize int, tenants []models.Tenant) []*models.Object {
   166  	objects := make([]*models.Object, 0, batchSize)
   167  	for i := 0; i < batchSize; i++ {
   168  		objects = append(objects, &models.Object{
   169  			Class: class,
   170  			ID:    strfmt.UUID(uuid.New().String()),
   171  			Properties: map[string]interface{}{
   172  				"counter": i,
   173  				"name":    tenants[i%len(tenants)].Name,
   174  			},
   175  			Tenant: tenants[i%len(tenants)].Name,
   176  		})
   177  	}
   178  	return objects
   179  }