Skip to content

Commit 19bc88a

Browse files
Improve collection and query loading; fix reflection panic for certain faker methods that require arguments
1 parent d5af4c4 commit 19bc88a

File tree

4 files changed

+92
-50
lines changed

4 files changed

+92
-50
lines changed

README.md

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,38 @@ The additional collection and query definitions can be found here:
157157
* [collections](./resources/collections/)
158158
* [queries](./resources/queries/)
159159

160-
### 4. Docker & Kubernetes
160+
161+
### 4. Workload Configuration & Loading
162+
163+
You can supply your own collections and queries using the `PLGM_COLLECTIONS_PATH` and `PLGM_QUERIES_PATH` environment variables (or the corresponding config file fields).
164+
165+
plgm supports two loading modes:
166+
167+
#### 1. Single File Mode
168+
If you point to a specific file, plgm will load **only** that file, regardless of its name.
169+
170+
```bash
171+
# Loads only my_custom_workload.json
172+
export PLGM_COLLECTIONS_PATH="./resources/collections/my_custom_workload.json"
173+
```
174+
175+
#### 2. Directory Mode (Multi-file)
176+
If you point to a folder, plgm will scan and merge **all** `.json` files found in that folder. This allows you to split complex schemas across multiple files.
177+
178+
```bash
179+
# Loads all .json files in the /custom folder
180+
export PLGM_COLLECTIONS_PATH="./resources/custom_collections/"
181+
```
182+
183+
#### Default Workload Filtering
184+
When using **Directory Mode**, the behavior depends on the `PLGM_DEFAULT_WORKLOAD` setting:
185+
186+
* **`true` (Default):** Loads **all** JSON files in the directory, including `default.json`.
187+
* **`false` (Custom):** Loads all JSON files **except** `default.json`.
188+
* *Use Case:* You can keep the example `default.json` in your folder for reference but exclude it from your actual test run by setting `PLGM_DEFAULT_WORKLOAD=false`.
189+
190+
191+
### 5. Docker & Kubernetes
161192
Prefer running in a container? We have a dedicated guide for building Docker images and running performance jobs directly inside Kubernetes (recommended for accurate network latency testing).
162193

163194
[View the Docker & Kubernetes Guide](docker.md)

internal/config/collections.go

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,13 @@ type CollectionsFile struct {
4343
Collections []CollectionDefinition `json:"collections"`
4444
}
4545

46-
// LoadCollections filters files based on the 'loadDefault' flag (from DefaultWorkload config).
46+
// LoadCollections loads files from a path.
47+
// If path is a folder:
48+
// - If loadDefault is TRUE: Loads ALL .json files (including default.json).
49+
// - If loadDefault is FALSE: Loads ALL .json files EXCEPT default.json.
50+
//
51+
// If path is a file:
52+
// - Loads the file unconditionally.
4753
func LoadCollections(path string, loadDefault bool) (*CollectionsFile, error) {
4854
if path == "" {
4955
return &CollectionsFile{}, nil
@@ -67,11 +73,11 @@ func LoadCollections(path string, loadDefault bool) (*CollectionsFile, error) {
6773
}
6874

6975
isDefault := strings.EqualFold(entry.Name(), "default.json")
70-
if loadDefault && !isDefault {
71-
continue // Skip non-default files
72-
}
76+
77+
// If default_workload is FALSE, we explicitly ignore 'default.json' in folder mode.
78+
// If default_workload is TRUE, we load everything (no continue).
7379
if !loadDefault && isDefault {
74-
continue // Skip default file
80+
continue
7581
}
7682

7783
fullPath := filepath.Join(path, entry.Name())
@@ -82,25 +88,12 @@ func LoadCollections(path string, loadDefault bool) (*CollectionsFile, error) {
8288
allCollections = append(allCollections, loaded.Collections...)
8389
}
8490
} else {
85-
// Single file: apply filtering logic to the specific file
86-
filename := filepath.Base(path)
87-
isDefault := strings.EqualFold(filename, "default.json")
88-
89-
shouldLoad := true
90-
if loadDefault && !isDefault {
91-
shouldLoad = false
92-
}
93-
if !loadDefault && isDefault {
94-
shouldLoad = false
95-
}
96-
97-
if shouldLoad {
98-
loaded, err := loadCollectionsFromFile(path)
99-
if err != nil {
100-
return nil, err
101-
}
102-
allCollections = append(allCollections, loaded.Collections...)
91+
// Single file: Always load it if the user specifically pointed to it.
92+
loaded, err := loadCollectionsFromFile(path)
93+
if err != nil {
94+
return nil, err
10395
}
96+
allCollections = append(allCollections, loaded.Collections...)
10497
}
10598

10699
return &CollectionsFile{Collections: allCollections}, nil

internal/config/queries.go

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,13 @@ type QueriesFile struct {
2424
Queries []QueryDefinition
2525
}
2626

27-
// LoadQueries filters files based on the 'loadDefault' flag.
27+
// LoadQueries loads files from a path.
28+
// If path is a folder:
29+
// - If loadDefault is TRUE: Loads ALL .json files (including default.json).
30+
// - If loadDefault is FALSE: Loads ALL .json files EXCEPT default.json.
31+
//
32+
// If path is a file:
33+
// - Loads the file unconditionally.
2834
func LoadQueries(path string, loadDefault bool) (*QueriesFile, error) {
2935
if path == "" {
3036
return &QueriesFile{}, nil
@@ -48,9 +54,9 @@ func LoadQueries(path string, loadDefault bool) (*QueriesFile, error) {
4854
}
4955

5056
isDefault := strings.EqualFold(entry.Name(), "default.json")
51-
if loadDefault && !isDefault {
52-
continue
53-
}
57+
58+
// If default_workload is FALSE, we explicitly ignore 'default.json' in folder mode.
59+
// If default_workload is TRUE, we load everything.
5460
if !loadDefault && isDefault {
5561
continue
5662
}
@@ -63,24 +69,12 @@ func LoadQueries(path string, loadDefault bool) (*QueriesFile, error) {
6369
allQueries = append(allQueries, loaded.Queries...)
6470
}
6571
} else {
66-
filename := filepath.Base(path)
67-
isDefault := strings.EqualFold(filename, "default.json")
68-
69-
shouldLoad := true
70-
if loadDefault && !isDefault {
71-
shouldLoad = false
72-
}
73-
if !loadDefault && isDefault {
74-
shouldLoad = false
75-
}
76-
77-
if shouldLoad {
78-
loaded, err := loadQueriesFromFile(path)
79-
if err != nil {
80-
return nil, err
81-
}
82-
allQueries = append(allQueries, loaded.Queries...)
72+
// Single file: Always load it.
73+
loaded, err := loadQueriesFromFile(path)
74+
if err != nil {
75+
return nil, err
8376
}
77+
allQueries = append(allQueries, loaded.Queries...)
8478
}
8579

8680
return &QueriesFile{Queries: allQueries}, nil

internal/datagen/random.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,36 @@ func RandomValueWithFaker(def config.CollectionField, faker *gofakeit.Faker) int
3838
method := fakerVal.MethodByName(methodName)
3939

4040
if method.IsValid() {
41-
results := method.Call(nil)
42-
if len(results) > 0 {
43-
return results[0].Interface()
41+
// Check argument count to prevent panics
42+
numArgs := method.Type().NumIn()
43+
44+
if numArgs == 0 {
45+
// e.g. Name(), Email() - No args required
46+
results := method.Call(nil)
47+
if len(results) > 0 {
48+
return results[0].Interface()
49+
}
50+
} else if numArgs == 1 && method.Type().In(0).Kind() == reflect.Int {
51+
// e.g. Sentence(wordCount) or Paragraph(paragraphCount)
52+
// Smart logic: Use configuration constraints if available
53+
argVal := 5 // Default fallback
54+
55+
if def.MaxLength > 0 {
56+
argVal = def.MaxLength
57+
} else if def.ArraySize > 0 {
58+
argVal = def.ArraySize
59+
}
60+
61+
results := method.Call([]reflect.Value{reflect.ValueOf(argVal)})
62+
if len(results) > 0 {
63+
return results[0].Interface()
64+
}
4465
}
66+
// Note: Methods requiring >1 args or non-int args are skipped here
67+
// and will fall through to the switch/default below.
4568
}
4669

47-
// Fallback for special providers
70+
// Fallback for special providers (handling cases reflection might miss or specific overrides)
4871
switch strings.ToLower(def.Provider) {
4972
case "uuid":
5073
return faker.UUID()
@@ -91,7 +114,8 @@ func RandomValueWithFaker(def config.CollectionField, faker *gofakeit.Faker) int
91114
if def.Provider == "" {
92115
return fmt.Sprintf("str-%d", rng.Intn(100000))
93116
}
94-
return "val" // Should be handled by provider logic
117+
// If provider was invalid or skipped in reflection, we land here.
118+
return "val"
95119

96120
case "bool", "boolean":
97121
return rng.Intn(2) == 0

0 commit comments

Comments
 (0)