Skip to content

Commit 24415d2

Browse files
committed
Improved performance
1 parent 29a6012 commit 24415d2

File tree

10 files changed

+168
-169
lines changed

10 files changed

+168
-169
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414
### Breaking changes
1515

1616
- The old version of string template in `type_params` of `string` type is no longer supported,
17-
instead you should use `{{ pattern('pattern_expression') }}`
17+
`{{ pattern('pattern_expression') }}` should be used instead.
1818

1919
## [0.0.1](https://github.com/tarantool/sdvg/compare/36d0930..0.0.1) - 2025-07-21
2020

doc/ru/usage.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,7 @@ open_ai:
162162
- `logical_type`: Логический тип строки. Поддерживаемые значения: `first_name`, `last_name`, `phone`, `text`.
163163
- `template`: Jinja-подобный шаблон для генерации строки. Позволяет использовать любые поля генерируемой модели и
164164
задавать паттерн строки с помощью функции `pattern`. Информация о фильтрах и функциях, доступных в шаблонных
165-
строках описана [здесь](#фильтры-и-функции-используемые-в-шаблонных-строках).
166-
Также поддерживается использование фильтров, таких как `upper` и `lower`.
165+
строках описана в конце данного раздела.
167166
- `locale`: Локаль для генерации строк. Поддерживаемые значения: `ru`, `en`. По умолчанию `en`.
168167
- `without_large_letters`: Флаг, указывающий, исключать ли большие буквы из строки.
169168
- `without_small_letters`: Флаг, указывающий, исключать ли маленькие буквы из строки.
@@ -244,7 +243,7 @@ open_ai:
244243
Подобна структуре для формата `http`, за исключением того,
245244
что поле `format_template` неизменяемое и всегда равняется значению по умолчанию.
246245

247-
#### Фильтры и функции, используемые в шаблонных строках
246+
Фильтры и функции, используемые в шаблонных строках
248247

249248
Шаблонные строки реализованы с использованием библиотеки `pongo2`, ознакомиться
250249
со всеми доступными фильтрами и функциями можно в репозитории [pongo2](https://github.com/flosch/pongo2).
@@ -254,7 +253,7 @@ open_ai:
254253
- pattern: позволяет создать паттерн строки при помощи специальных символов.
255254
Символ `A` - любая большая буква, символ `a` - любая маленькая буква,
256255
символ `0` - любая цифра, символ `#` - любой символ, а остальные символы остаются как есть.
257-
Функция доступна только в поле `template` типа данных `string`.
256+
Функция доступна только в поле `template` типа данных `string`.
258257

259258
#### Примеры конфигурации генерации данных
260259

internal/generator/common/utils.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"time"
1515

1616
"github.com/google/uuid"
17+
"github.com/otaviokr/topological-sort/toposort"
1718
"github.com/pkg/errors"
1819
"gopkg.in/yaml.v3"
1920
)
@@ -378,3 +379,37 @@ func ExtractValuesFromTemplate(template string) []string {
378379

379380
return values
380381
}
382+
383+
// TopologicalSort sorts the given items in topological order using the provided
384+
// function to extract node name and dependencies.
385+
// Returns the sorted node names, a flag indicating if any dependencies exist,
386+
// and an error if a cycle is detected.
387+
func TopologicalSort[T any](items []T, nodeFunc func(T) (string, []string)) ([]string, bool, error) {
388+
var (
389+
graph = make(map[string][]string, len(items))
390+
sortedVertexes = make([]string, 0, len(items))
391+
hasDependencies bool
392+
err error
393+
)
394+
395+
for _, item := range items {
396+
name, dependencies := nodeFunc(item)
397+
if len(dependencies) > 0 {
398+
hasDependencies = true
399+
}
400+
401+
sortedVertexes = append(sortedVertexes, name)
402+
graph[name] = dependencies
403+
}
404+
405+
if !hasDependencies {
406+
return sortedVertexes, false, nil
407+
}
408+
409+
sortedVertexes, err = toposort.ReverseTarjan(graph)
410+
if err != nil {
411+
return nil, false, errors.New(err.Error())
412+
}
413+
414+
return sortedVertexes, hasDependencies, nil
415+
}

internal/generator/common/utils_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,3 +744,77 @@ func TestExtractValuesFromTemplate(t *testing.T) {
744744
t.Run(tc.name, func(t *testing.T) { testFunc(t, tc) })
745745
}
746746
}
747+
748+
func TestTopologicalSort(t *testing.T) {
749+
type node struct {
750+
name string
751+
deps []string
752+
}
753+
754+
type testCase struct {
755+
name string
756+
items []node
757+
wantErr bool
758+
wantDependencies bool
759+
expected []string
760+
}
761+
762+
testCases := []testCase{
763+
{
764+
name: "Empty items",
765+
items: []node{},
766+
wantErr: false,
767+
wantDependencies: false,
768+
expected: []string{},
769+
},
770+
{
771+
name: "Items with dependencies",
772+
items: []node{
773+
{name: "1", deps: []string{"3"}},
774+
{name: "2", deps: []string{"4"}},
775+
{name: "3", deps: []string{"2"}},
776+
{name: "4", deps: []string{}},
777+
},
778+
wantErr: false,
779+
wantDependencies: true,
780+
expected: []string{"4", "2", "3", "1"},
781+
},
782+
{
783+
name: "Items without dependencies",
784+
items: []node{
785+
{name: "1", deps: []string{}},
786+
{name: "2", deps: []string{}},
787+
{name: "3", deps: []string{}},
788+
},
789+
wantErr: false,
790+
wantDependencies: false,
791+
expected: []string{"1", "2", "3"},
792+
},
793+
{
794+
name: "Items with cycle dependencies",
795+
items: []node{
796+
{name: "1", deps: []string{"2"}},
797+
{name: "2", deps: []string{"1"}},
798+
},
799+
wantErr: true,
800+
wantDependencies: false,
801+
expected: nil,
802+
},
803+
}
804+
805+
testFunc := func(t *testing.T, tc testCase) {
806+
t.Helper()
807+
808+
actual, hasDependencies, err := TopologicalSort(tc.items, func(node node) (string, []string) {
809+
return node.name, node.deps
810+
})
811+
812+
require.Equal(t, tc.wantErr, err != nil)
813+
require.Equal(t, tc.wantDependencies, hasDependencies)
814+
require.Equal(t, tc.expected, actual)
815+
}
816+
817+
for _, tc := range testCases {
818+
t.Run(tc.name, func(t *testing.T) { testFunc(t, tc) })
819+
}
820+
}

internal/generator/models/common.go

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ import (
99
"strings"
1010

1111
"github.com/ilyakaznacheev/cleanenv"
12-
"github.com/otaviokr/topological-sort/toposort"
1312
"github.com/pkg/errors"
14-
"github.com/tarantool/sdvg/internal/generator/common"
1513
"gopkg.in/yaml.v3"
1614
)
1715

@@ -121,25 +119,3 @@ func parseErrsToString(errs []error) string {
121119

122120
return sb.String()
123121
}
124-
125-
func TopologicalSort(columns []*Column) ([]string, error) {
126-
graph := make(map[string][]string, len(columns))
127-
for _, c := range columns {
128-
graph[c.Name] = make([]string, 0)
129-
130-
for _, r := range c.Ranges {
131-
if r.StringParams == nil || r.StringParams.Template == "" {
132-
continue
133-
}
134-
135-
graph[c.Name] = common.ExtractValuesFromTemplate(r.StringParams.Template)
136-
}
137-
}
138-
139-
sortedVertexes, err := toposort.ReverseTarjan(graph)
140-
if err != nil {
141-
return nil, errors.New(err.Error())
142-
}
143-
144-
return sortedVertexes, nil
145-
}

internal/generator/models/common_test.go

Lines changed: 0 additions & 117 deletions
This file was deleted.

internal/generator/usecase/general/generator/generator.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,9 @@ func newRangeGenerator(
149149
distinctValuesCount = uint64(generatorValuesCount)
150150
}
151151

152-
distinctValuesCountByColumn[column.Name] += distinctValuesCount
152+
if distinctValuesCountByColumn != nil {
153+
distinctValuesCountByColumn[column.Name] += distinctValuesCount
154+
}
153155

154156
rangeOrdered := dataRange.Ordered
155157
orderSeed := dataColumnSeed
@@ -201,7 +203,7 @@ type valueID struct {
201203
type BatchGenerator struct {
202204
numbers []valueID
203205
nextNumber int
204-
valuer func(number valueID, generatedValues map[string]any) (any, error)
206+
valuer func(number valueID, rowValues map[string]any) (any, error)
205207
}
206208

207209
func (cg *ColumnGenerator) NewBatchGenerator(batchSize uint64) *BatchGenerator {
@@ -227,14 +229,14 @@ func (cg *ColumnGenerator) NewBatchGenerator(batchSize uint64) *BatchGenerator {
227229
}
228230
}
229231

230-
valuer := func(id valueID, generatedValues map[string]any) (any, error) {
232+
valuer := func(id valueID, rowValues map[string]any) (any, error) {
231233
vg := cg.rangeGenerators[id.generatorIndex]
232234

233235
if vg.nullPercentage > 0 && fastRandomFloat(cg.dataColumnSeed+uint64(id.number)) < vg.nullPercentage {
234236
return nil, nil //nolint:nilnil
235237
}
236238

237-
return vg.generator.Value(id.number, generatedValues)
239+
return vg.generator.Value(id.number, rowValues)
238240
}
239241

240242
return &BatchGenerator{
@@ -244,8 +246,8 @@ func (cg *ColumnGenerator) NewBatchGenerator(batchSize uint64) *BatchGenerator {
244246
}
245247

246248
// Value returns random value for described column.
247-
func (g *BatchGenerator) Value(generatedValues map[string]any) (any, error) {
248-
res, err := g.valuer(g.numbers[g.nextNumber], generatedValues)
249+
func (g *BatchGenerator) Value(rowValues map[string]any) (any, error) {
250+
res, err := g.valuer(g.numbers[g.nextNumber], rowValues)
249251
g.nextNumber++
250252
g.nextNumber %= len(g.numbers)
251253

internal/generator/usecase/general/generator/value/interfaces.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ type Generator interface {
77
// SetTotalCount method should remember count of rows to generate
88
SetTotalCount(totalValuesCount uint64) error
99
// Value method should return ordered unique value by number
10-
Value(number float64, generatedValues map[string]any) (any, error)
10+
Value(number float64, rowValues map[string]any) (any, error)
1111
// ValuesCount method should return the number of possible values to generate
1212
ValuesCount(distinctValuesCountByColumn map[string]uint64) float64
1313
}

0 commit comments

Comments
 (0)