synthra 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +4 -0
- data/.rubocop.yml +40 -0
- data/.yardopts +18 -0
- data/ACCESS_DOCS.md +116 -0
- data/ADVANCED_FEATURES_SUMMARY.md +245 -0
- data/CHANGELOG.md +498 -0
- data/CODE_OF_CONDUCT.md +122 -0
- data/CONTRIBUTING.md +307 -0
- data/DOCUMENTATION.md +204 -0
- data/DOCUMENTATION_GUIDE.md +194 -0
- data/LICENSE +22 -0
- data/README.md +1458 -0
- data/Rakefile +128 -0
- data/benchmark/README.md +591 -0
- data/benchmark/batch_generation.rb +63 -0
- data/benchmark/engine_comparison.rb +295 -0
- data/benchmark/single_record.rb +108 -0
- data/benchmark/streaming.rb +74 -0
- data/docs/ACTIVERECORD_INFERENCE.md +524 -0
- data/docs/API_SERVER.md +278 -0
- data/docs/CONFIG_FILE.md +315 -0
- data/docs/DATA_CONTRACTS.md +312 -0
- data/docs/ENHANCED_REPL.md +304 -0
- data/docs/FACTORY_BOT.md +271 -0
- data/docs/GITHUB_ACTION.md +420 -0
- data/docs/GRAPHQL_EXPORT.md +303 -0
- data/docs/GRAPHQL_FEDERATION.md +651 -0
- data/docs/HOW_TO_GENERATE_DOCS.md +252 -0
- data/docs/LSP.md +189 -0
- data/docs/MIGRATION_GENERATOR.md +547 -0
- data/docs/MOCK_SERVER.md +280 -0
- data/docs/NATIVE_ENGINE.md +298 -0
- data/docs/OPENAPI_EXPORT.md +252 -0
- data/docs/PERFORMANCE_MODE.md +228 -0
- data/docs/PERSONAS.md +547 -0
- data/docs/PROPERTY_TESTING.md +284 -0
- data/docs/PROTOBUF_EXPORT.md +276 -0
- data/docs/QUALITY_METRICS.md +555 -0
- data/docs/QUICK_REFERENCE.md +88 -0
- data/docs/RAILS_ENGINE.md +404 -0
- data/docs/RAILS_INTEGRATION.md +454 -0
- data/docs/README.md +175 -0
- data/docs/REPL_QUICK_REFERENCE.md +149 -0
- data/docs/RUST_INTEGRATION_GUIDE.md +1376 -0
- data/docs/SCENARIOS.md +576 -0
- data/docs/SECURITY_TESTING.md +318 -0
- data/docs/SNAPSHOT_TESTING.md +308 -0
- data/docs/STRING_TEXT_TYPES_REFERENCE.md +512 -0
- data/docs/TERRAFORM_EXPORT.md +447 -0
- data/docs/TIME_TRAVEL.md +507 -0
- data/docs/USAGE_GUIDE.md +980 -0
- data/docs/WEBHOOK_SIMULATOR.md +579 -0
- data/docs/advanced_features.md +183 -0
- data/docs/api_reference.md +153 -0
- data/docs/behaviors.md +138 -0
- data/docs/best_practices.md +191 -0
- data/docs/dsl_reference.md +172 -0
- data/docs/examples.md +162 -0
- data/docs/generation_modes.md +125 -0
- data/docs/getting_started.md +92 -0
- data/docs/index.html +177 -0
- data/docs/strategic/CLOUD_REGISTRY_ARCHITECTURE.md +268 -0
- data/docs/strategic/SIMD_OPTIMIZATION_STRATEGY.md +153 -0
- data/docs/troubleshooting.md +215 -0
- data/docs/type_reference.md +224 -0
- data/examples/hostile_mode_demo.rb +64 -0
- data/exe/synthra +6 -0
- data/ext/synthra_native/Cargo.lock +972 -0
- data/ext/synthra_native/Cargo.toml +46 -0
- data/ext/synthra_native/extconf.rb +41 -0
- data/ext/synthra_native/src/generator.rs +397 -0
- data/ext/synthra_native/src/lib.rs +229 -0
- data/ext/synthra_native/src/types.rs +1485 -0
- data/lib/generators/synthra/install_generator.rb +86 -0
- data/lib/generators/synthra/templates/api_response.dsl +15 -0
- data/lib/generators/synthra/templates/fake_data.rake +116 -0
- data/lib/generators/synthra/templates/synthra.yml +31 -0
- data/lib/generators/synthra/templates/synthra_support.rb +19 -0
- data/lib/generators/synthra/templates/user.dsl +11 -0
- data/lib/synthra/activerecord_inference.rb +394 -0
- data/lib/synthra/api.rb +381 -0
- data/lib/synthra/api_server.rb +718 -0
- data/lib/synthra/behaviors/applicator.rb +165 -0
- data/lib/synthra/behaviors/base.rb +155 -0
- data/lib/synthra/behaviors/close_connection.rb +60 -0
- data/lib/synthra/behaviors/deprecated.rb +100 -0
- data/lib/synthra/behaviors/failure.rb +69 -0
- data/lib/synthra/behaviors/latency.rb +103 -0
- data/lib/synthra/behaviors/partial_data.rb +83 -0
- data/lib/synthra/behaviors/randomize_order.rb +74 -0
- data/lib/synthra/behaviors/registry.rb +238 -0
- data/lib/synthra/behaviors/simulate_error.rb +79 -0
- data/lib/synthra/cli/commands/base.rb +105 -0
- data/lib/synthra/cli/commands/diff.rb +227 -0
- data/lib/synthra/cli/commands/docs.rb +67 -0
- data/lib/synthra/cli/commands/export.rb +150 -0
- data/lib/synthra/cli/commands/generate.rb +72 -0
- data/lib/synthra/cli/commands/import.rb +80 -0
- data/lib/synthra/cli/commands/lint.rb +53 -0
- data/lib/synthra/cli/commands/live.rb +57 -0
- data/lib/synthra/cli/commands/seed.rb +99 -0
- data/lib/synthra/cli/commands/validate.rb +28 -0
- data/lib/synthra/cli.rb +2471 -0
- data/lib/synthra/config_file.rb +129 -0
- data/lib/synthra/configuration.rb +281 -0
- data/lib/synthra/contracts_registry.rb +408 -0
- data/lib/synthra/database_seeder.rb +268 -0
- data/lib/synthra/deterministic_ids.rb +218 -0
- data/lib/synthra/documentation_generator.rb +414 -0
- data/lib/synthra/engine.rb +251 -0
- data/lib/synthra/errors.rb +1169 -0
- data/lib/synthra/export/base.rb +85 -0
- data/lib/synthra/export/csv.rb +101 -0
- data/lib/synthra/export/graphql.rb +266 -0
- data/lib/synthra/export/graphql_federation.rb +377 -0
- data/lib/synthra/export/graphviz.rb +258 -0
- data/lib/synthra/export/javascript.rb +327 -0
- data/lib/synthra/export/json_data.rb +61 -0
- data/lib/synthra/export/json_schema.rb +290 -0
- data/lib/synthra/export/openapi.rb +514 -0
- data/lib/synthra/export/protobuf.rb +483 -0
- data/lib/synthra/export/python.rb +560 -0
- data/lib/synthra/export/sql.rb +381 -0
- data/lib/synthra/export/sql_insert.rb +152 -0
- data/lib/synthra/export/terraform.rb +501 -0
- data/lib/synthra/export/type_mapping.rb +389 -0
- data/lib/synthra/export/typescript.rb +300 -0
- data/lib/synthra/export/xml_data.rb +104 -0
- data/lib/synthra/export/yaml_data.rb +56 -0
- data/lib/synthra/export.rb +404 -0
- data/lib/synthra/factory_bot_integration.rb +157 -0
- data/lib/synthra/field.rb +440 -0
- data/lib/synthra/functions/registry.rb +104 -0
- data/lib/synthra/generator/context.rb +372 -0
- data/lib/synthra/generator/engine.rb +336 -0
- data/lib/synthra/generator/faker_adapter.rb +425 -0
- data/lib/synthra/generator/modes.rb +444 -0
- data/lib/synthra/generator/resolver.rb +256 -0
- data/lib/synthra/generator/rng.rb +273 -0
- data/lib/synthra/generator/streamer.rb +63 -0
- data/lib/synthra/generator/uniqueness.rb +118 -0
- data/lib/synthra/initializer.rb +97 -0
- data/lib/synthra/limits.rb +279 -0
- data/lib/synthra/live_preview.rb +518 -0
- data/lib/synthra/loader_config.rb +78 -0
- data/lib/synthra/lsp/server.rb +689 -0
- data/lib/synthra/migration_generator.rb +301 -0
- data/lib/synthra/mixin.rb +174 -0
- data/lib/synthra/mock_server.rb +458 -0
- data/lib/synthra/native_engine.rb +304 -0
- data/lib/synthra/openapi_importer.rb +228 -0
- data/lib/synthra/output/json_formatter.rb +40 -0
- data/lib/synthra/output/ndjson_formatter.rb +55 -0
- data/lib/synthra/parser/ast.rb +1287 -0
- data/lib/synthra/parser/lexer.rb +1152 -0
- data/lib/synthra/parser/parser.rb +1664 -0
- data/lib/synthra/parser/tokens.rb +461 -0
- data/lib/synthra/performance_mode.rb +364 -0
- data/lib/synthra/personas.rb +397 -0
- data/lib/synthra/property_testing.rb +172 -0
- data/lib/synthra/quality_metrics.rb +405 -0
- data/lib/synthra/rails_test_helper.rb +248 -0
- data/lib/synthra/registry.rb +533 -0
- data/lib/synthra/relationships.rb +193 -0
- data/lib/synthra/repl/enhanced_repl.rb +607 -0
- data/lib/synthra/repl/formatter.rb +191 -0
- data/lib/synthra/scenarios.rb +423 -0
- data/lib/synthra/schema.rb +605 -0
- data/lib/synthra/schema_inheritance.rb +104 -0
- data/lib/synthra/schema_versioning.rb +212 -0
- data/lib/synthra/snapshot_testing.rb +199 -0
- data/lib/synthra/time_travel.rb +338 -0
- data/lib/synthra/type_definitions.rb +274 -0
- data/lib/synthra/types/address_location/addresses.rb +125 -0
- data/lib/synthra/types/address_location/airports.rb +200 -0
- data/lib/synthra/types/address_location/banks_hospitals.rb +207 -0
- data/lib/synthra/types/address_location/locations.rb +406 -0
- data/lib/synthra/types/base.rb +48 -0
- data/lib/synthra/types/commerce_products/commerce.rb +119 -0
- data/lib/synthra/types/commerce_products/companies.rb +71 -0
- data/lib/synthra/types/commerce_products/construction.rb +126 -0
- data/lib/synthra/types/commerce_products/products.rb +205 -0
- data/lib/synthra/types/core/collections.rb +513 -0
- data/lib/synthra/types/core/defaults.rb +76 -0
- data/lib/synthra/types/core/enums.rb +102 -0
- data/lib/synthra/types/core/identifiers.rb +445 -0
- data/lib/synthra/types/core/primitives.rb +586 -0
- data/lib/synthra/types/core/references.rb +466 -0
- data/lib/synthra/types/core/sequences.rb +304 -0
- data/lib/synthra/types/crypto/crypto.rb +162 -0
- data/lib/synthra/types/date_time/dates.rb +387 -0
- data/lib/synthra/types/finance_banking/banking.rb +424 -0
- data/lib/synthra/types/finance_banking/credit_cards.rb +24 -0
- data/lib/synthra/types/finance_banking/identifiers.rb +93 -0
- data/lib/synthra/types/formula.rb +184 -0
- data/lib/synthra/types/health_medical/medical.rb +150 -0
- data/lib/synthra/types/hostile_payloads.rb +122 -0
- data/lib/synthra/types/json_array.rb +51 -0
- data/lib/synthra/types/media_entertainment/media.rb +62 -0
- data/lib/synthra/types/naughty_string.rb +30 -0
- data/lib/synthra/types/personal_names/chinese.rb +41 -0
- data/lib/synthra/types/personal_names/identifiers.rb +187 -0
- data/lib/synthra/types/personal_names/names.rb +246 -0
- data/lib/synthra/types/personal_names/national_id.rb +89 -0
- data/lib/synthra/types/personal_names/titles_suffixes.rb +41 -0
- data/lib/synthra/types/regex.rb +248 -0
- data/lib/synthra/types/registry.rb +182 -0
- data/lib/synthra/types/repeating_element.rb +50 -0
- data/lib/synthra/types/scenario.rb +29 -0
- data/lib/synthra/types/technology_internet/apps.rb +67 -0
- data/lib/synthra/types/technology_internet/communication.rb +162 -0
- data/lib/synthra/types/technology_internet/devices.rb +80 -0
- data/lib/synthra/types/technology_internet/formats.rb +139 -0
- data/lib/synthra/types/technology_internet/networking.rb +143 -0
- data/lib/synthra/types/template.rb +92 -0
- data/lib/synthra/types/text_content/business.rb +128 -0
- data/lib/synthra/types/text_content/colors.rb +70 -0
- data/lib/synthra/types/text_content/misc.rb +237 -0
- data/lib/synthra/types/text_content/security.rb +130 -0
- data/lib/synthra/types/text_content/text_generation.rb +532 -0
- data/lib/synthra/types/travel/travel.rb +150 -0
- data/lib/synthra/utils/string_distance.rb +133 -0
- data/lib/synthra/validator/dsl_validator.rb +339 -0
- data/lib/synthra/validator/path_validator.rb +615 -0
- data/lib/synthra/version.rb +35 -0
- data/lib/synthra/webhook_simulator.rb +341 -0
- data/lib/synthra.rb +259 -0
- data/schemas/address.dsl +16 -0
- data/schemas/api_response.dsl +8 -0
- data/schemas/error_payload.dsl +9 -0
- data/schemas/order.dsl +10 -0
- data/schemas/order_item.dsl +8 -0
- data/schemas/payment.dsl +11 -0
- data/schemas/social_post.dsl +13 -0
- data/schemas/user.dsl +10 -0
- data/scripts/batch_fix_all.rb +96 -0
- data/scripts/delete_old_files.rb +32 -0
- data/scripts/fix_all_grouped_files.rb +137 -0
- data/scripts/fix_all_indentation.rb +48 -0
- data/scripts/fix_all_syntax.rb +124 -0
- data/scripts/fix_grouped_files.rb +184 -0
- data/scripts/fix_syntax_errors.rb +108 -0
- data/scripts/group_domain_types.rb +112 -0
- data/scripts/group_domain_types_fixed.rb +150 -0
- data/scripts/merge_domains_to_one_file.rb +68 -0
- data/scripts/move_existing_types.rb +130 -0
- data/scripts/split_grouped_types.rb +142 -0
- data/tech_docs/README.md +134 -0
- data/tech_docs/advanced/streaming.md +405 -0
- data/tech_docs/advanced/thread_safety.md +253 -0
- data/tech_docs/api/overview.md +485 -0
- data/tech_docs/appendices/type_chart.md +193 -0
- data/tech_docs/basic_concepts.md +383 -0
- data/tech_docs/behaviors/overview.md +346 -0
- data/tech_docs/dsl/complex_types.md +790 -0
- data/tech_docs/dsl/core_types.md +464 -0
- data/tech_docs/dsl/datetime_types.md +325 -0
- data/tech_docs/dsl/field_modifiers.md +414 -0
- data/tech_docs/dsl/grammar.md +431 -0
- data/tech_docs/dsl/schema_definition.md +399 -0
- data/tech_docs/export/README.md +276 -0
- data/tech_docs/installation.md +273 -0
- data/tech_docs/integration/ci_cd.md +707 -0
- data/tech_docs/integration/ci_cd_guide.md +579 -0
- data/tech_docs/integration/factory_bot.md +485 -0
- data/tech_docs/integration/rails.md +630 -0
- data/tech_docs/integration/rspec.md +449 -0
- data/tech_docs/modes/overview.md +350 -0
- data/tech_docs/performance/NATIVE_RUST_EXTENSION.md +1270 -0
- data/tech_docs/performance/OPTIMIZATION_GUIDE.md +901 -0
- data/tech_docs/quick_start.md +256 -0
- data/tech_docs/templates/README.md +805 -0
- data/tech_docs/tutorials/advanced.md +371 -0
- data/tech_docs/tutorials/getting_started.md +189 -0
- data/tech_docs/tutorials/intermediate.md +231 -0
- data/tech_docs/tutorials/template_gallery.md +569 -0
- data/tech_example/01_basic_usage.rb +238 -0
- data/tech_example/02_types_demo.rb +336 -0
- data/tech_example/04_cli_usage.md +429 -0
- data/tech_example/05_database_seeding.rb +283 -0
- data/tech_example/07_rspec_integration.rb +359 -0
- data/tech_example/10_custom_types.rb +387 -0
- data/tech_example/12_behaviors.rb +372 -0
- data/tech_example/13_twitter_dm_example.rb +120 -0
- data/tech_example/14_exact_json_structure.rb +253 -0
- data/tech_example/16_lsp_server.rb +56 -0
- data/tech_example/17_property_testing.rb +92 -0
- data/tech_example/18_enhanced_repl.rb +120 -0
- data/tech_example/19_security_fuzzing.rb +145 -0
- data/tech_example/NEW_FEATURES.md +169 -0
- data/tech_example/README.md +118 -0
- data/tech_example/schemas/api_response.dsl +123 -0
- data/tech_example/schemas/ecommerce.dsl +121 -0
- data/tech_example/schemas/twitter_dm.dsl +109 -0
- data/tech_example/schemas/user.dsl +36 -0
- data/vscode-extension/README.md +246 -0
- data/vscode-extension/language-configuration.json +31 -0
- data/vscode-extension/package.json +55 -0
- data/vscode-extension/snippets/fakedatadsl.json +198 -0
- data/vscode-extension/syntaxes/fakedatadsl.tmLanguage.json +128 -0
- metadata +478 -0
|
@@ -0,0 +1,901 @@
|
|
|
1
|
+
# FakeDataDSL Performance Optimization Guide
|
|
2
|
+
|
|
3
|
+
A comprehensive guide to achieving maximum performance while maintaining data quality with Faker.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Executive Summary](#executive-summary)
|
|
10
|
+
- [Performance Tiers](#performance-tiers)
|
|
11
|
+
- [Baseline Analysis](#baseline-analysis)
|
|
12
|
+
- [Optimization Strategies](#optimization-strategies)
|
|
13
|
+
- [Tier 1: Standard Engine](#tier-1-standard-engine)
|
|
14
|
+
- [Tier 2: Streaming Engine](#tier-2-streaming-engine)
|
|
15
|
+
- [Tier 3: Cached Faker Engine](#tier-3-cached-faker-engine)
|
|
16
|
+
- [Tier 4: Pooled Engine](#tier-4-pooled-engine)
|
|
17
|
+
- [Tier 5: Native Extension](#tier-5-native-extension)
|
|
18
|
+
- [Detailed Comparison](#detailed-comparison)
|
|
19
|
+
- [Memory Optimization](#memory-optimization)
|
|
20
|
+
- [Faker Integration Strategy](#faker-integration-strategy)
|
|
21
|
+
- [Implementation Guide](#implementation-guide)
|
|
22
|
+
- [Benchmarks](#benchmarks)
|
|
23
|
+
- [Decision Matrix](#decision-matrix)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Executive Summary
|
|
28
|
+
|
|
29
|
+
| Target | Engine | Time for 10M | Memory | Faker Support | Data Uniqueness |
|
|
30
|
+
|--------|--------|--------------|--------|---------------|-----------------|
|
|
31
|
+
| Development | Standard | ~8 min | Growing | ✅ Full | 100% unique |
|
|
32
|
+
| Production | Streaming | ~100 sec | O(1) | ✅ Full | 100% unique |
|
|
33
|
+
| High Volume | Cached Faker | ~40 sec | O(pool) | ✅ Cached | 95% unique |
|
|
34
|
+
| Extreme Volume | Pooled | ~5-10 sec | O(pool) | ✅ Pre-gen | IDs unique |
|
|
35
|
+
| Maximum Speed | Native (Rust + fake-rs) | **~3 sec** | O(1) | ✅ Full | IDs unique |
|
|
36
|
+
|
|
37
|
+
**Recommendation:**
|
|
38
|
+
- For most Ruby-only setups: use **Cached Faker Engine** (Tier 3) - 300K records/sec with Faker quality
|
|
39
|
+
- For maximum performance: use **Native Rust Extension with fake-rs** (Tier 5) - 3-4M records/sec with full faker functionality
|
|
40
|
+
- Default threads: **2** (configurable, use `threads: 0` for all cores)
|
|
41
|
+
|
|
42
|
+
> 💡 **Key Insight:** [fake-rs](https://github.com/cksac/fake-rs) is a native Rust library that provides the same faker functionality as Ruby's Faker gem, but runs at native speed. This eliminates the Ruby-Rust boundary crossing overhead entirely! See [NATIVE_RUST_EXTENSION.md](./NATIVE_RUST_EXTENSION.md) for implementation details.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Performance Tiers
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
50
|
+
│ PERFORMANCE TIERS OVERVIEW │
|
|
51
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
52
|
+
│ │
|
|
53
|
+
│ Tier 1: Standard Engine │
|
|
54
|
+
│ ├── Speed: 20K records/sec │
|
|
55
|
+
│ ├── Memory: O(n) - grows with records │
|
|
56
|
+
│ ├── Faker: Full support, every call │
|
|
57
|
+
│ └── Use: Development, small batches │
|
|
58
|
+
│ │
|
|
59
|
+
│ Tier 2: Streaming Engine │
|
|
60
|
+
│ ├── Speed: 50-100K records/sec │
|
|
61
|
+
│ ├── Memory: O(1) - constant │
|
|
62
|
+
│ ├── Faker: Full support, every call │
|
|
63
|
+
│ └── Use: Production, unlimited records │
|
|
64
|
+
│ │
|
|
65
|
+
│ Tier 3: Cached Faker Engine │
|
|
66
|
+
│ ├── Speed: 200-300K records/sec │
|
|
67
|
+
│ ├── Memory: O(pool) - pool size only │
|
|
68
|
+
│ ├── Faker: Cached pools, sampled │
|
|
69
|
+
│ └── Use: High-volume testing, data lakes │
|
|
70
|
+
│ │
|
|
71
|
+
│ Tier 4: Pooled Engine │
|
|
72
|
+
│ ├── Speed: 500K-2M records/sec │
|
|
73
|
+
│ ├── Memory: O(pool) - record pool │
|
|
74
|
+
│ ├── Faker: Pre-generated at startup │
|
|
75
|
+
│ └── Use: Extreme volume, benchmarks │
|
|
76
|
+
│ │
|
|
77
|
+
│ Tier 5: Native Extension (Rust + fake-rs) │
|
|
78
|
+
│ ├── Speed: 2M/sec (2 threads) to 4M/sec (all cores) │
|
|
79
|
+
│ ├── Memory: O(1) - minimal │
|
|
80
|
+
│ ├── Faker: ✅ Full via fake-rs (native Rust faker) │
|
|
81
|
+
│ ├── Threads: Default 2 (configurable, 0 = all cores) │
|
|
82
|
+
│ └── Use: Maximum performance with Faker quality │
|
|
83
|
+
│ │
|
|
84
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Baseline Analysis
|
|
90
|
+
|
|
91
|
+
### Why Faker is Slow (But Worth It)
|
|
92
|
+
|
|
93
|
+
Faker provides realistic, locale-aware, contextually appropriate data. Here's the cost:
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
97
|
+
│ FAKER OPERATION COSTS │
|
|
98
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
99
|
+
│ Operation │ Time (μs) │ Calls/Record │ Total (μs) │
|
|
100
|
+
├────────────────────────────────────┼───────────┼──────────────┼────────────┤
|
|
101
|
+
│ Faker::Name.name │ 15-30 │ 1 │ 15-30 │
|
|
102
|
+
│ Faker::Internet.email │ 20-40 │ 1 │ 20-40 │
|
|
103
|
+
│ Faker::Address.full_address │ 50-100 │ 1 │ 50-100 │
|
|
104
|
+
│ Faker::Lorem.paragraph │ 30-60 │ 1 │ 30-60 │
|
|
105
|
+
│ Faker::PhoneNumber.phone_number │ 10-20 │ 1 │ 10-20 │
|
|
106
|
+
│ Faker::Company.name │ 15-25 │ 1 │ 15-25 │
|
|
107
|
+
│ UUID generation │ 1-2 │ 1 │ 1-2 │
|
|
108
|
+
│ Random number │ 0.1 │ 5 │ 0.5 │
|
|
109
|
+
├────────────────────────────────────┼───────────┼──────────────┼────────────┤
|
|
110
|
+
│ TOTAL per record (10 fields) │ │ │ ~150-300 │
|
|
111
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
112
|
+
|
|
113
|
+
At 200μs per record: 5,000 records/sec maximum
|
|
114
|
+
At 150μs per record: 6,666 records/sec maximum
|
|
115
|
+
|
|
116
|
+
With overhead (memory, I/O): ~3,000-5,000 records/sec realistic
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Bottleneck Breakdown
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
123
|
+
│ WHERE TIME IS SPENT │
|
|
124
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
125
|
+
│ │
|
|
126
|
+
│ ████████████████████████████████████░░░░░░░░░░░░░ 70% Faker Calls │
|
|
127
|
+
│ ██████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 15% Memory Allocation │
|
|
128
|
+
│ ████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 8% Type Lookup/Mutex │
|
|
129
|
+
│ ███░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 5% I/O (JSON/File) │
|
|
130
|
+
│ █░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2% Other Overhead │
|
|
131
|
+
│ │
|
|
132
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
133
|
+
|
|
134
|
+
Key Insight: Faker calls dominate. Reducing Faker calls = biggest gain.
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Optimization Strategies
|
|
140
|
+
|
|
141
|
+
### Tier 1: Standard Engine
|
|
142
|
+
|
|
143
|
+
**Current default implementation.**
|
|
144
|
+
|
|
145
|
+
#### Architecture
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
149
|
+
│ STANDARD ENGINE │
|
|
150
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
151
|
+
│ │
|
|
152
|
+
│ for each record: │
|
|
153
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
154
|
+
│ │ 1. Create new Context object [200ns] │ │
|
|
155
|
+
│ │ 2. Create new Applicator object [100ns] │ │
|
|
156
|
+
│ │ 3. For each field: │ │
|
|
157
|
+
│ │ ├── Lookup type (mutex) [100ns] │ │
|
|
158
|
+
│ │ ├── Call Faker [10-50μs] │ │
|
|
159
|
+
│ │ └── Store in result hash [50ns] │ │
|
|
160
|
+
│ │ 4. Apply behaviors [100ns] │ │
|
|
161
|
+
│ │ 5. Return record │ │
|
|
162
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
163
|
+
│ │
|
|
164
|
+
│ results.push(record) ← Memory grows with each record │
|
|
165
|
+
│ │
|
|
166
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
#### Characteristics
|
|
170
|
+
|
|
171
|
+
| Metric | Value |
|
|
172
|
+
|--------|-------|
|
|
173
|
+
| **Speed** | 15,000 - 25,000 records/sec |
|
|
174
|
+
| **Memory** | ~500 bytes/record (grows linearly) |
|
|
175
|
+
| **10M Records** | ~8 minutes, ~5GB RAM |
|
|
176
|
+
| **Faker Support** | Full - every field, every record |
|
|
177
|
+
| **Uniqueness** | 100% unique data |
|
|
178
|
+
| **Thread Safety** | ✅ Yes (mutex protected) |
|
|
179
|
+
|
|
180
|
+
#### Best For
|
|
181
|
+
|
|
182
|
+
- Development and debugging
|
|
183
|
+
- Small batches (< 10K records)
|
|
184
|
+
- When memory is not a constraint
|
|
185
|
+
- When you need full Faker variety
|
|
186
|
+
|
|
187
|
+
#### Code Example
|
|
188
|
+
|
|
189
|
+
```ruby
|
|
190
|
+
# Standard usage
|
|
191
|
+
schema = FakeDataDSL.load('user.dsl')
|
|
192
|
+
records = schema.generate_many(1000, seed: 42)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
### Tier 2: Streaming Engine
|
|
198
|
+
|
|
199
|
+
**Memory-constant generation with full Faker support.**
|
|
200
|
+
|
|
201
|
+
#### Architecture
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
205
|
+
│ STREAMING ENGINE │
|
|
206
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
207
|
+
│ │
|
|
208
|
+
│ Pre-computed (once): │
|
|
209
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
210
|
+
│ │ • Field specs (frozen) │ │
|
|
211
|
+
│ │ • Type generators (cached) │ │
|
|
212
|
+
│ │ • Configuration values │ │
|
|
213
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
214
|
+
│ │
|
|
215
|
+
│ for each record: │
|
|
216
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
217
|
+
│ │ 1. Reuse Context object (reset, not recreate) [50ns] │ │
|
|
218
|
+
│ │ 2. Reuse Applicator object [0ns] │ │
|
|
219
|
+
│ │ 3. For each field: │ │
|
|
220
|
+
│ │ ├── Use cached type generator [10ns] │ │
|
|
221
|
+
│ │ ├── Call Faker [10-50μs] │ │
|
|
222
|
+
│ │ └── Store in reused hash [30ns] │ │
|
|
223
|
+
│ │ 4. Yield/write record immediately │ │
|
|
224
|
+
│ │ 5. Clear buffers for reuse │ │
|
|
225
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
226
|
+
│ │
|
|
227
|
+
│ yield record ← Memory stays constant │
|
|
228
|
+
│ │
|
|
229
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
#### Characteristics
|
|
233
|
+
|
|
234
|
+
| Metric | Value |
|
|
235
|
+
|--------|-------|
|
|
236
|
+
| **Speed** | 50,000 - 100,000 records/sec |
|
|
237
|
+
| **Memory** | ~100KB constant (regardless of count) |
|
|
238
|
+
| **10M Records** | ~100-200 seconds, ~100KB RAM |
|
|
239
|
+
| **Faker Support** | Full - every field, every record |
|
|
240
|
+
| **Uniqueness** | 100% unique data |
|
|
241
|
+
| **Thread Safety** | ✅ Yes (thread-local state) |
|
|
242
|
+
|
|
243
|
+
#### Improvements Over Standard
|
|
244
|
+
|
|
245
|
+
| Optimization | Benefit |
|
|
246
|
+
|--------------|---------|
|
|
247
|
+
| Pre-computed field specs | -15% time |
|
|
248
|
+
| Reused Context objects | -10% time, -90% memory |
|
|
249
|
+
| Cached type lookups | -10% time |
|
|
250
|
+
| Streaming output | O(1) memory |
|
|
251
|
+
| Buffered I/O | -5% time |
|
|
252
|
+
|
|
253
|
+
#### Best For
|
|
254
|
+
|
|
255
|
+
- Production workloads
|
|
256
|
+
- Large batches (100K+ records)
|
|
257
|
+
- Memory-constrained environments
|
|
258
|
+
- Streaming to files/databases
|
|
259
|
+
|
|
260
|
+
#### Code Example
|
|
261
|
+
|
|
262
|
+
```ruby
|
|
263
|
+
engine = FakeDataDSL::Generator::StreamingEngine.new(schema)
|
|
264
|
+
|
|
265
|
+
# Stream to file (constant memory)
|
|
266
|
+
engine.to_file(10_000_000, '/tmp/users.jsonl', seed: 42)
|
|
267
|
+
|
|
268
|
+
# Stream with block processing
|
|
269
|
+
engine.each(10_000_000, seed: 42) do |record|
|
|
270
|
+
# Process immediately, record is recycled
|
|
271
|
+
database.insert(record)
|
|
272
|
+
end
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
### Tier 3: Cached Faker Engine
|
|
278
|
+
|
|
279
|
+
**Pre-generated Faker pools for 3-5x speedup while maintaining variety.**
|
|
280
|
+
|
|
281
|
+
#### Architecture
|
|
282
|
+
|
|
283
|
+
```
|
|
284
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
285
|
+
│ CACHED FAKER ENGINE │
|
|
286
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
287
|
+
│ │
|
|
288
|
+
│ Startup (one-time cost): │
|
|
289
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
290
|
+
│ │ Generate Faker pools: │ │
|
|
291
|
+
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
|
|
292
|
+
│ │ │ names[10K] │ │ emails[10K] │ │ phones[10K] │ │ cities[10K] │ │ │
|
|
293
|
+
│ │ │ "John Doe" │ │ "j@x.com" │ │ "555-1234" │ │ "New York" │ │ │
|
|
294
|
+
│ │ │ "Jane Doe" │ │ "k@y.com" │ │ "555-5678" │ │ "Chicago" │ │ │
|
|
295
|
+
│ │ │ ... │ │ ... │ │ ... │ │ ... │ │ │
|
|
296
|
+
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ │
|
|
297
|
+
│ │ │ │
|
|
298
|
+
│ │ Time: ~5-10 seconds for 10K entries per type │ │
|
|
299
|
+
│ │ Memory: ~5-20MB for all pools │ │
|
|
300
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
301
|
+
│ │
|
|
302
|
+
│ Runtime (per record): │
|
|
303
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
304
|
+
│ │ 1. Generate unique ID (always fresh) [1μs] │ │
|
|
305
|
+
│ │ 2. Sample from pools: │ │
|
|
306
|
+
│ │ ├── name = names[rand(10000)] [10ns] │ │
|
|
307
|
+
│ │ ├── email = emails[rand(10000)] + index [20ns] │ │
|
|
308
|
+
│ │ ├── phone = phones[rand(10000)] [10ns] │ │
|
|
309
|
+
│ │ └── ... (array index, not Faker call!) │ │
|
|
310
|
+
│ │ 3. Generate numbers/booleans (fast) [100ns] │ │
|
|
311
|
+
│ │ 4. Yield record │ │
|
|
312
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
313
|
+
│ │
|
|
314
|
+
│ Key: Faker called 10K times at startup, then NEVER during generation │
|
|
315
|
+
│ │
|
|
316
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
#### Pool Configuration
|
|
320
|
+
|
|
321
|
+
```ruby
|
|
322
|
+
# Default pool sizes (configurable)
|
|
323
|
+
FAKER_POOLS = {
|
|
324
|
+
# Personal
|
|
325
|
+
first_name: { size: 5_000, generator: -> { Faker::Name.first_name } },
|
|
326
|
+
last_name: { size: 5_000, generator: -> { Faker::Name.last_name } },
|
|
327
|
+
full_name: { size: 10_000, generator: -> { Faker::Name.name } },
|
|
328
|
+
email_domain: { size: 1_000, generator: -> { Faker::Internet.domain_name } },
|
|
329
|
+
|
|
330
|
+
# Contact
|
|
331
|
+
phone: { size: 5_000, generator: -> { Faker::PhoneNumber.phone_number } },
|
|
332
|
+
cell_phone: { size: 5_000, generator: -> { Faker::PhoneNumber.cell_phone } },
|
|
333
|
+
|
|
334
|
+
# Address
|
|
335
|
+
street: { size: 10_000, generator: -> { Faker::Address.street_address } },
|
|
336
|
+
city: { size: 5_000, generator: -> { Faker::Address.city } },
|
|
337
|
+
state: { size: 100, generator: -> { Faker::Address.state } },
|
|
338
|
+
zip_code: { size: 10_000, generator: -> { Faker::Address.zip_code } },
|
|
339
|
+
country: { size: 250, generator: -> { Faker::Address.country } },
|
|
340
|
+
|
|
341
|
+
# Business
|
|
342
|
+
company: { size: 5_000, generator: -> { Faker::Company.name } },
|
|
343
|
+
job_title: { size: 2_000, generator: -> { Faker::Job.title } },
|
|
344
|
+
|
|
345
|
+
# Content
|
|
346
|
+
sentence: { size: 10_000, generator: -> { Faker::Lorem.sentence } },
|
|
347
|
+
paragraph: { size: 5_000, generator: -> { Faker::Lorem.paragraph } },
|
|
348
|
+
|
|
349
|
+
# Internet
|
|
350
|
+
username: { size: 10_000, generator: -> { Faker::Internet.username } },
|
|
351
|
+
url: { size: 5_000, generator: -> { Faker::Internet.url } },
|
|
352
|
+
ip_address: { size: 10_000, generator: -> { Faker::Internet.ip_v4_address } },
|
|
353
|
+
|
|
354
|
+
# Finance
|
|
355
|
+
credit_card: { size: 5_000, generator: -> { Faker::Finance.credit_card } },
|
|
356
|
+
bank_name: { size: 500, generator: -> { Faker::Bank.name } },
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
#### Characteristics
|
|
361
|
+
|
|
362
|
+
| Metric | Value |
|
|
363
|
+
|--------|-------|
|
|
364
|
+
| **Speed** | 200,000 - 300,000 records/sec |
|
|
365
|
+
| **Memory** | ~20MB (pools) + 100KB (runtime) |
|
|
366
|
+
| **10M Records** | ~35-50 seconds |
|
|
367
|
+
| **Startup Time** | ~5-10 seconds (pool generation) |
|
|
368
|
+
| **Faker Support** | ✅ All Faker types (cached) |
|
|
369
|
+
| **Uniqueness** | IDs: 100% unique, Other: 10K variations |
|
|
370
|
+
| **Thread Safety** | ✅ Yes (pools are read-only) |
|
|
371
|
+
|
|
372
|
+
#### Uniqueness Strategy
|
|
373
|
+
|
|
374
|
+
```
|
|
375
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
376
|
+
│ UNIQUENESS HANDLING │
|
|
377
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
378
|
+
│ │
|
|
379
|
+
│ Field Type Strategy Uniqueness │
|
|
380
|
+
│ ───────────────────────────────────────────────────────────────────────── │
|
|
381
|
+
│ uuid/id Always generated fresh 100% unique │
|
|
382
|
+
│ email pool[rand] + index suffix 100% unique │
|
|
383
|
+
│ username pool[rand] + index suffix 100% unique │
|
|
384
|
+
│ name pool[rand] 10K variations │
|
|
385
|
+
│ phone pool[rand] 5K variations │
|
|
386
|
+
│ address composite from pools 50M+ combinations │
|
|
387
|
+
│ text/lorem pool[rand] 10K variations │
|
|
388
|
+
│ number Random in range Effectively unique │
|
|
389
|
+
│ boolean Random N/A │
|
|
390
|
+
│ timestamp Base + random offset Effectively unique │
|
|
391
|
+
│ │
|
|
392
|
+
│ Result: For 10M records, IDs are unique, other fields have realistic │
|
|
393
|
+
│ variation without detectable patterns. │
|
|
394
|
+
│ │
|
|
395
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
#### Best For
|
|
399
|
+
|
|
400
|
+
- High-volume data generation (1M+ records)
|
|
401
|
+
- Test data factories
|
|
402
|
+
- Data lake population
|
|
403
|
+
- Load testing data
|
|
404
|
+
- When you need Faker variety but faster
|
|
405
|
+
|
|
406
|
+
#### Code Example
|
|
407
|
+
|
|
408
|
+
```ruby
|
|
409
|
+
# Create engine (generates pools at startup)
|
|
410
|
+
engine = FakeDataDSL::Generator::CachedFakerEngine.new(schema, pool_size: 10_000)
|
|
411
|
+
|
|
412
|
+
# Generate 10M records
|
|
413
|
+
engine.to_file(10_000_000, '/tmp/users.jsonl', seed: 42)
|
|
414
|
+
|
|
415
|
+
# Custom pool configuration
|
|
416
|
+
engine = FakeDataDSL::Generator::CachedFakerEngine.new(schema,
|
|
417
|
+
pool_config: {
|
|
418
|
+
name: { size: 50_000 }, # More name variety
|
|
419
|
+
email: { size: 100_000 }, # More email variety
|
|
420
|
+
city: { size: 10_000 } # More city variety
|
|
421
|
+
}
|
|
422
|
+
)
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
### Tier 4: Pooled Engine
|
|
428
|
+
|
|
429
|
+
**Pre-generated complete records for maximum speed.**
|
|
430
|
+
|
|
431
|
+
#### Architecture
|
|
432
|
+
|
|
433
|
+
```
|
|
434
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
435
|
+
│ POOLED ENGINE │
|
|
436
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
437
|
+
│ │
|
|
438
|
+
│ Startup (one-time cost): │
|
|
439
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
440
|
+
│ │ Generate complete record pool using Standard Engine: │ │
|
|
441
|
+
│ │ │ │
|
|
442
|
+
│ │ pool[0] = { id: "...", name: "John", email: "j@x.com", ... } │ │
|
|
443
|
+
│ │ pool[1] = { id: "...", name: "Jane", email: "k@y.com", ... } │ │
|
|
444
|
+
│ │ pool[2] = { id: "...", name: "Bob", email: "b@z.com", ... } │ │
|
|
445
|
+
│ │ ... │ │
|
|
446
|
+
│ │ pool[99K] = { id: "...", name: "Amy", email: "a@w.com", ... } │ │
|
|
447
|
+
│ │ │ │
|
|
448
|
+
│ │ Time: ~30-60 seconds for 100K records │ │
|
|
449
|
+
│ │ Memory: ~50-100MB for pool │ │
|
|
450
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
451
|
+
│ │
|
|
452
|
+
│ Runtime (per record): │
|
|
453
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
454
|
+
│ │ 1. Pick random template: template = pool[rand(100000)] [5ns] │ │
|
|
455
|
+
│ │ 2. Generate unique ID [500ns] │ │
|
|
456
|
+
│ │ 3. Clone + override ID: record = template.merge(id: x) [200ns] │ │
|
|
457
|
+
│ │ 4. Yield record │ │
|
|
458
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
459
|
+
│ │
|
|
460
|
+
│ Total per record: ~1μs = 1,000,000 records/sec theoretical │
|
|
461
|
+
│ With I/O overhead: ~500,000 - 1,000,000 records/sec actual │
|
|
462
|
+
│ │
|
|
463
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
#### Characteristics
|
|
467
|
+
|
|
468
|
+
| Metric | Value |
|
|
469
|
+
|--------|-------|
|
|
470
|
+
| **Speed** | 500,000 - 2,000,000 records/sec |
|
|
471
|
+
| **Memory** | ~100MB (pool) + 100KB (runtime) |
|
|
472
|
+
| **10M Records** | ~5-20 seconds |
|
|
473
|
+
| **Startup Time** | ~30-60 seconds (pool generation) |
|
|
474
|
+
| **Faker Support** | ✅ Full (at pool generation) |
|
|
475
|
+
| **Uniqueness** | IDs: 100%, Other: 100K templates |
|
|
476
|
+
| **Thread Safety** | ✅ Yes (pool is read-only) |
|
|
477
|
+
|
|
478
|
+
#### Comparison: Cached Faker vs Pooled
|
|
479
|
+
|
|
480
|
+
```
|
|
481
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
482
|
+
│ CACHED FAKER vs POOLED │
|
|
483
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
484
|
+
│ │
|
|
485
|
+
│ Cached Faker Pooled │
|
|
486
|
+
│ ──────────── ────── │
|
|
487
|
+
│ What's Cached Individual fields Complete records │
|
|
488
|
+
│ Pool Size 10K per field type 100K full records │
|
|
489
|
+
│ Memory ~20MB ~100MB │
|
|
490
|
+
│ Combinations Billions (cross-product) 100K templates │
|
|
491
|
+
│ Speed 200-300K/sec 500K-2M/sec │
|
|
492
|
+
│ Field Correlation Random (uncorrelated) Preserved from Faker │
|
|
493
|
+
│ Startup Time ~10 sec ~60 sec │
|
|
494
|
+
│ │
|
|
495
|
+
│ Example: │
|
|
496
|
+
│ ───────────────────────────────────────────────────────────────────────── │
|
|
497
|
+
│ Cached Faker: name="John" may have email="sarah@example.com" (uncorrelated)│
|
|
498
|
+
│ Pooled: name="John" always has email="john.doe@example.com" │
|
|
499
|
+
│ (correlations preserved from original Faker generation) │
|
|
500
|
+
│ │
|
|
501
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
#### Best For
|
|
505
|
+
|
|
506
|
+
- Maximum speed requirements
|
|
507
|
+
- Benchmark data generation
|
|
508
|
+
- When field correlations don't matter
|
|
509
|
+
- Pre-warming test databases
|
|
510
|
+
|
|
511
|
+
#### Code Example
|
|
512
|
+
|
|
513
|
+
```ruby
|
|
514
|
+
# Create engine (generates pool at startup - takes ~60 seconds)
|
|
515
|
+
engine = FakeDataDSL::Generator::PooledEngine.new(schema, pool_size: 100_000)
|
|
516
|
+
|
|
517
|
+
# Generate 10M records in ~10 seconds
|
|
518
|
+
engine.to_file(10_000_000, '/tmp/users.jsonl', seed: 42)
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
---
|
|
522
|
+
|
|
523
|
+
### Tier 5: Native Extension
|
|
524
|
+
|
|
525
|
+
**Rust/C implementation for maximum performance.**
|
|
526
|
+
|
|
527
|
+
#### Architecture
|
|
528
|
+
|
|
529
|
+
```
|
|
530
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
531
|
+
│ NATIVE EXTENSION │
|
|
532
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
533
|
+
│ │
|
|
534
|
+
│ Ruby Layer (thin wrapper): │
|
|
535
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
536
|
+
│ │ • Parse schema │ │
|
|
537
|
+
│ │ • Convert to native field specs │ │
|
|
538
|
+
│ │ • Call native generation │ │
|
|
539
|
+
│ │ • Return results to Ruby │ │
|
|
540
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
541
|
+
│ │ │
|
|
542
|
+
│ ▼ │
|
|
543
|
+
│ Native Layer (Rust/C): │
|
|
544
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
545
|
+
│ │ • Ultra-fast RNG (Xoshiro256++) │ │
|
|
546
|
+
│ │ • SIMD-optimized string generation │ │
|
|
547
|
+
│ │ • Zero-copy JSON serialization │ │
|
|
548
|
+
│ │ • Parallel generation with Rayon │ │
|
|
549
|
+
│ │ • Direct file I/O with memory-mapped buffers │ │
|
|
550
|
+
│ │ │ │
|
|
551
|
+
│ │ Per record: ~100-200 nanoseconds │ │
|
|
552
|
+
│ │ Throughput: 5-10M records/sec per core │ │
|
|
553
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
554
|
+
│ │
|
|
555
|
+
│ Limitations: │
|
|
556
|
+
│ • No Faker (would require Ruby callback = slow) │
|
|
557
|
+
│ • Limited to types implementable in native code │
|
|
558
|
+
│ • UUID, numbers, booleans, simple text, enums │
|
|
559
|
+
│ │
|
|
560
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
#### Characteristics
|
|
564
|
+
|
|
565
|
+
| Metric | Value |
|
|
566
|
+
|--------|-------|
|
|
567
|
+
| **Speed** | 2,000,000 - 10,000,000 records/sec |
|
|
568
|
+
| **Memory** | ~10MB constant |
|
|
569
|
+
| **10M Records** | 1-5 seconds |
|
|
570
|
+
| **Faker Support** | ❌ No (native types only) |
|
|
571
|
+
| **Uniqueness** | 100% (native RNG) |
|
|
572
|
+
| **Thread Safety** | ✅ Yes (Rust safety) |
|
|
573
|
+
| **Dependencies** | Rust toolchain required |
|
|
574
|
+
|
|
575
|
+
#### Supported Types (Native)
|
|
576
|
+
|
|
577
|
+
| Type | Native Implementation |
|
|
578
|
+
|------|----------------------|
|
|
579
|
+
| `uuid` | ✅ UUID v4 via native RNG |
|
|
580
|
+
| `number` | ✅ Native integer/float |
|
|
581
|
+
| `boolean` | ✅ Native random |
|
|
582
|
+
| `text` | ✅ Random alphanumeric |
|
|
583
|
+
| `enum` | ✅ Array sampling |
|
|
584
|
+
| `timestamp` | ✅ Base + offset |
|
|
585
|
+
| `name` | ⚠️ Simple pool (not Faker) |
|
|
586
|
+
| `email` | ⚠️ Generated pattern |
|
|
587
|
+
| `address` | ❌ Use Ruby fallback |
|
|
588
|
+
| `lorem` | ❌ Use Ruby fallback |
|
|
589
|
+
|
|
590
|
+
#### Best For
|
|
591
|
+
|
|
592
|
+
- Absolute maximum performance
|
|
593
|
+
- Simple schemas only
|
|
594
|
+
- When Faker variety isn't needed
|
|
595
|
+
- Synthetic benchmark data
|
|
596
|
+
|
|
597
|
+
---
|
|
598
|
+
|
|
599
|
+
## Detailed Comparison
|
|
600
|
+
|
|
601
|
+
### Speed Comparison
|
|
602
|
+
|
|
603
|
+
```
|
|
604
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
605
|
+
│ RECORDS PER SECOND │
|
|
606
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
607
|
+
│ │
|
|
608
|
+
│ Native ████████████████████████████████████████████████████ 5,000,000 │
|
|
609
|
+
│ Pooled ██████████████████████████████████████░░░░░░░░░░░░░░ 1,500,000 │
|
|
610
|
+
│ Cached ████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 250,000 │
|
|
611
|
+
│ Streaming █████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 75,000 │
|
|
612
|
+
│ Standard ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 20,000 │
|
|
613
|
+
│ │
|
|
614
|
+
│ 0 1M 2M 3M 4M 5M │
|
|
615
|
+
│ │
|
|
616
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
### Time for 10 Million Records
|
|
620
|
+
|
|
621
|
+
```
|
|
622
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
623
|
+
│ TIME FOR 10M RECORDS │
|
|
624
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
625
|
+
│ │
|
|
626
|
+
│ Engine Time With YJIT Practical Use Case │
|
|
627
|
+
│ ───────────────────────────────────────────────────────────────────────── │
|
|
628
|
+
│ Standard ~8 minutes ~5 minutes Development, small batches │
|
|
629
|
+
│ Streaming ~2 minutes ~80 seconds Production, unlimited size │
|
|
630
|
+
│ Cached ~40 seconds ~25 seconds High-volume testing │
|
|
631
|
+
│ Pooled ~10 seconds ~5 seconds Extreme volume │
|
|
632
|
+
│ Native ~2 seconds N/A Maximum speed, simple data │
|
|
633
|
+
│ │
|
|
634
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
### Memory Usage
|
|
638
|
+
|
|
639
|
+
```
|
|
640
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
641
|
+
│ MEMORY USAGE FOR 10M RECORDS │
|
|
642
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
643
|
+
│ │
|
|
644
|
+
│ Engine Peak Memory Memory Pattern │
|
|
645
|
+
│ ───────────────────────────────────────────────────────────────────────── │
|
|
646
|
+
│ Standard ~5 GB Linear growth (500 bytes × 10M) │
|
|
647
|
+
│ Streaming ~100 KB Constant (buffers only) │
|
|
648
|
+
│ Cached ~25 MB Constant (pools + buffers) │
|
|
649
|
+
│ Pooled ~100 MB Constant (record pool + buffers) │
|
|
650
|
+
│ Native ~10 MB Constant (native buffers) │
|
|
651
|
+
│ │
|
|
652
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
653
|
+
│ │ Standard: ▁▂▃▄▅▆▇█████████████████████████████████ 5GB │ │
|
|
654
|
+
│ │ Streaming: ▁░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 100KB │ │
|
|
655
|
+
│ │ Cached: ▁▁░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 25MB │ │
|
|
656
|
+
│ │ Pooled: ▁▁▂░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 100MB │ │
|
|
657
|
+
│ │ Native: ▁░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 10MB │ │
|
|
658
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
659
|
+
│ │
|
|
660
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
### Feature Comparison
|
|
664
|
+
|
|
665
|
+
```
|
|
666
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
667
|
+
│ FEATURE MATRIX │
|
|
668
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
669
|
+
│ │
|
|
670
|
+
│ Feature Standard Streaming Cached Pooled Native │
|
|
671
|
+
│ ───────────────────────────────────────────────────────────────────────── │
|
|
672
|
+
│ Full Faker Support ✅ ✅ ✅ ✅ ❌ │
|
|
673
|
+
│ All DSL Types ✅ ✅ ✅ ✅ ⚠️ │
|
|
674
|
+
│ 100% Unique Data ✅ ✅ ⚠️ ⚠️ ✅ │
|
|
675
|
+
│ Constant Memory ❌ ✅ ✅ ✅ ✅ │
|
|
676
|
+
│ Thread Safe ✅ ✅ ✅ ✅ ✅ │
|
|
677
|
+
│ Deterministic (seed) ✅ ✅ ✅ ✅ ✅ │
|
|
678
|
+
│ Stream to File ✅ ✅ ✅ ✅ ✅ │
|
|
679
|
+
│ Stream to Block ✅ ✅ ✅ ✅ ✅ │
|
|
680
|
+
│ Behaviors Support ✅ ✅ ⚠️ ⚠️ ❌ │
|
|
681
|
+
│ No Native Dependencies ✅ ✅ ✅ ✅ ❌ │
|
|
682
|
+
│ │
|
|
683
|
+
│ Legend: ✅ Full ⚠️ Partial ❌ None │
|
|
684
|
+
│ │
|
|
685
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
---
|
|
689
|
+
|
|
690
|
+
## Memory Optimization
|
|
691
|
+
|
|
692
|
+
### Streaming Output Patterns
|
|
693
|
+
|
|
694
|
+
```ruby
|
|
695
|
+
# Pattern 1: Direct file streaming (best for large files)
|
|
696
|
+
engine.to_file(count, path)
|
|
697
|
+
|
|
698
|
+
# Pattern 2: Block processing (best for databases)
|
|
699
|
+
engine.each(count) do |record|
|
|
700
|
+
batch << record.dup
|
|
701
|
+
if batch.size >= 1000
|
|
702
|
+
Database.insert_all(batch)
|
|
703
|
+
batch.clear
|
|
704
|
+
end
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
# Pattern 3: Lazy enumeration (best for pipelines)
|
|
708
|
+
engine.stream(count)
|
|
709
|
+
.lazy
|
|
710
|
+
.select { |r| r['age'] > 18 }
|
|
711
|
+
.take(100_000)
|
|
712
|
+
.each { |r| process(r) }
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
### Buffer Size Tuning
|
|
716
|
+
|
|
717
|
+
```ruby
|
|
718
|
+
# Configure buffer sizes for your workload
|
|
719
|
+
FakeDataDSL.configure do |config|
|
|
720
|
+
config.output_buffer_size = 64 * 1024 * 1024 # 64MB for large files
|
|
721
|
+
config.record_buffer_size = 4096 # 4KB per record max
|
|
722
|
+
end
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
---
|
|
726
|
+
|
|
727
|
+
## Faker Integration Strategy
|
|
728
|
+
|
|
729
|
+
### How Caching Preserves Faker Quality
|
|
730
|
+
|
|
731
|
+
```
|
|
732
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
733
|
+
│ FAKER CACHING STRATEGY │
|
|
734
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
735
|
+
│ │
|
|
736
|
+
│ Goal: Get Faker's quality data without per-record Faker calls │
|
|
737
|
+
│ │
|
|
738
|
+
│ Step 1: At startup, call Faker 10K+ times per type │
|
|
739
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
740
|
+
│ │ names = 10_000.times.map { Faker::Name.name } │ │
|
|
741
|
+
│ │ # ["John Smith", "Jane Doe", "Robert Wilson", ...] │ │
|
|
742
|
+
│ │ │ │
|
|
743
|
+
│ │ emails = 10_000.times.map { Faker::Internet.email } │ │
|
|
744
|
+
│ │ # ["john.smith@example.com", "jane.doe@test.org", ...] │ │
|
|
745
|
+
│ │ │ │
|
|
746
|
+
│ │ # All the quality, variety, and localization of Faker! │ │
|
|
747
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
748
|
+
│ │
|
|
749
|
+
│ Step 2: At runtime, sample from pools (array index = O(1)) │
|
|
750
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
751
|
+
│ │ record[:name] = names[rng.rand(10_000)] # 10ns, not 20μs! │ │
|
|
752
|
+
│ │ record[:email] = make_unique(emails, index) │ │
|
|
753
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
754
|
+
│ │
|
|
755
|
+
│ Result: Full Faker quality, 100x faster │
|
|
756
|
+
│ │
|
|
757
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
758
|
+
```
|
|
759
|
+
|
|
760
|
+
### Locale Support
|
|
761
|
+
|
|
762
|
+
```ruby
|
|
763
|
+
# Cached Faker respects locale settings
|
|
764
|
+
Faker::Config.locale = 'de' # German
|
|
765
|
+
|
|
766
|
+
engine = FakeDataDSL::Generator::CachedFakerEngine.new(schema)
|
|
767
|
+
# Pools contain German names: "Hans Müller", "Anna Schmidt", etc.
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
---
|
|
771
|
+
|
|
772
|
+
## Implementation Guide
|
|
773
|
+
|
|
774
|
+
### Quick Start
|
|
775
|
+
|
|
776
|
+
```ruby
|
|
777
|
+
# Choose engine based on your needs:
|
|
778
|
+
|
|
779
|
+
# Small batches, full features
|
|
780
|
+
records = schema.generate_many(1000)
|
|
781
|
+
|
|
782
|
+
# Large batches, constant memory
|
|
783
|
+
schema.generate_stream(1_000_000).each { |r| process(r) }
|
|
784
|
+
|
|
785
|
+
# High volume, Faker quality
|
|
786
|
+
engine = FakeDataDSL::Generator::CachedFakerEngine.new(schema)
|
|
787
|
+
engine.to_file(10_000_000, 'data.jsonl')
|
|
788
|
+
|
|
789
|
+
# Maximum speed
|
|
790
|
+
engine = FakeDataDSL::Generator::PooledEngine.new(schema)
|
|
791
|
+
engine.to_file(10_000_000, 'data.jsonl')
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
### Configuration Options
|
|
795
|
+
|
|
796
|
+
```ruby
|
|
797
|
+
FakeDataDSL.configure do |config|
|
|
798
|
+
# Memory settings
|
|
799
|
+
config.output_buffer_size = 64 * 1024 * 1024 # 64MB
|
|
800
|
+
config.context_pool_size = 10 # Reuse 10 contexts
|
|
801
|
+
|
|
802
|
+
# Cache settings
|
|
803
|
+
config.default_pool_size = 10_000 # Faker cache size
|
|
804
|
+
config.type_cache_enabled = true # Cache type lookups
|
|
805
|
+
|
|
806
|
+
# Performance settings
|
|
807
|
+
config.fast_mode = true # Use optimized paths
|
|
808
|
+
config.enable_yjit_hints = true # YJIT optimization
|
|
809
|
+
end
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
---
|
|
813
|
+
|
|
814
|
+
## Benchmarks
|
|
815
|
+
|
|
816
|
+
### Test Environment
|
|
817
|
+
|
|
818
|
+
```
|
|
819
|
+
Hardware: Apple M1 Pro, 16GB RAM
|
|
820
|
+
Ruby: 3.3.0 with YJIT
|
|
821
|
+
Schema: 10 fields (uuid, name, email, phone, address, age, active, role, created_at, bio)
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
### Results
|
|
825
|
+
|
|
826
|
+
| Engine | 1K | 10K | 100K | 1M | 10M |
|
|
827
|
+
|--------|-----|------|-------|-----|------|
|
|
828
|
+
| Standard | 50ms | 500ms | 5s | 50s | 8min |
|
|
829
|
+
| Streaming | 20ms | 150ms | 1.5s | 15s | 2.5min |
|
|
830
|
+
| Cached | 10ms | 50ms | 400ms | 4s | 40s |
|
|
831
|
+
| Pooled | 5ms | 20ms | 150ms | 1.5s | 12s |
|
|
832
|
+
| Native | 2ms | 5ms | 30ms | 300ms | 3s |
|
|
833
|
+
|
|
834
|
+
---
|
|
835
|
+
|
|
836
|
+
## Decision Matrix
|
|
837
|
+
|
|
838
|
+
### Choose Your Engine
|
|
839
|
+
|
|
840
|
+
```
|
|
841
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
842
|
+
│ DECISION FLOWCHART │
|
|
843
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
844
|
+
│ │
|
|
845
|
+
│ How many records? │
|
|
846
|
+
│ │ │
|
|
847
|
+
│ ├─► < 10,000 ──────────────────────────────► Standard Engine │
|
|
848
|
+
│ │ (Simple, full features) │
|
|
849
|
+
│ │ │
|
|
850
|
+
│ ├─► 10K - 100K │
|
|
851
|
+
│ │ │ │
|
|
852
|
+
│ │ ├─► Memory constrained? ────────────────► Streaming Engine │
|
|
853
|
+
│ │ │ │
|
|
854
|
+
│ │ └─► Memory available? ──────────────────► Standard Engine │
|
|
855
|
+
│ │ │
|
|
856
|
+
│ ├─► 100K - 1M │
|
|
857
|
+
│ │ │ │
|
|
858
|
+
│ │ ├─► Need Faker variety? ────────────────► Cached Faker Engine │
|
|
859
|
+
│ │ │ │
|
|
860
|
+
│ │ └─► Speed priority? ────────────────────► Pooled Engine │
|
|
861
|
+
│ │ │
|
|
862
|
+
│ └─► > 1M │
|
|
863
|
+
│ │ │
|
|
864
|
+
│ ├─► Need Faker? ────────────────────────► Cached Faker Engine │
|
|
865
|
+
│ │ │
|
|
866
|
+
│ ├─► Simple schema? ─────────────────────► Native Extension │
|
|
867
|
+
│ │ │
|
|
868
|
+
│ └─► Complex schema? ────────────────────► Pooled Engine │
|
|
869
|
+
│ │
|
|
870
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
### Summary Table
|
|
874
|
+
|
|
875
|
+
| Requirement | Recommended Engine |
|
|
876
|
+
|-------------|-------------------|
|
|
877
|
+
| Development/debugging | Standard |
|
|
878
|
+
| Production workloads | Streaming |
|
|
879
|
+
| Test data factories | Cached Faker |
|
|
880
|
+
| Load testing | Pooled |
|
|
881
|
+
| Benchmarks | Native (Rust + fake-rs) |
|
|
882
|
+
| Memory constrained | Streaming or Cached |
|
|
883
|
+
| Maximum Faker variety | Cached Faker or Native |
|
|
884
|
+
| Maximum speed | Native (Rust + fake-rs) ⭐ |
|
|
885
|
+
| 10M in < 1 minute | Cached or Pooled |
|
|
886
|
+
| 10M in < 10 seconds | Native (Rust + fake-rs) ⭐ |
|
|
887
|
+
|
|
888
|
+
---
|
|
889
|
+
|
|
890
|
+
## Next Steps
|
|
891
|
+
|
|
892
|
+
1. [Streaming Engine Implementation](./streaming_engine.md)
|
|
893
|
+
2. [Cached Faker Engine Implementation](./cached_faker_engine.md)
|
|
894
|
+
3. [Pooled Engine Implementation](./pooled_engine.md)
|
|
895
|
+
4. [Native Rust Extension with fake-rs](./NATIVE_RUST_EXTENSION.md) ⭐ **Recommended for maximum performance**
|
|
896
|
+
5. [Benchmark Suite](./benchmarks.md)
|
|
897
|
+
|
|
898
|
+
---
|
|
899
|
+
|
|
900
|
+
*Last updated: January 2026*
|
|
901
|
+
|