Skip to content

Storage Configuration Guide (Data Storage)

📖 Complete guide to creating and configuring data storage for flexible management of settings, limits, features, and user data.

📑 Table of Contents


🎯 Purpose

Storage — flexible key-value data storage system that allows:

  • Store tenant settings (limits, parameters, configurations)
  • Manage tenant features and capabilities
  • Store user data and settings
  • Flexibly add new attributes without DB schema changes
  • Automatically synchronize data from configuration to DB

Advantages:

  • ✅ Flexibility — can add new attributes without DB migrations
  • ✅ Organization — data grouped by logical files (Tenant Storage) or by users (User Storage)
  • ✅ Simplicity — simple data types (strings, numbers, bool, arrays, objects)
  • ✅ Automatic type conversion on read
  • ✅ Data isolation — user data isolated by tenants

🔀 Storage Types

System supports two storage types:

Tenant Storage

  • Purpose: Store tenant settings and configurations
  • Structure: Grouping by group_key (settings, limits, features, etc.)
  • Data source: YAML files in storage/ folder of tenant configuration
  • Synchronization: Automatic synchronization from configuration to DB
  • Key: (tenant_id, group_key, key)

User Storage

  • Purpose: Store user data and settings
  • Structure: Flat structure without groups ({key: value})
  • Data source: Set programmatically via scenario actions
  • Synchronization: Not synchronized with configuration, managed only via actions
  • Key: (tenant_id, user_id, key)

Differences:

CharacteristicTenant StorageUser Storage
GroupingBy group_keyWithout groups
Data sourceYAML filesScenario actions
SynchronizationAutomatic from configurationManual management
Usage scopeTenant settingsUser data
Structure{group_key: {key: value}}{key: value}

🏢 Tenant Storage

Storage Structure

Location

Tenant Storage located in storage/ folder inside tenant configuration:

tenant_101/
├── tg_bot.yaml
├── storage/               # Storage folder
│   ├── settings.yaml     # Settings group
│   ├── limits.yaml       # Limits group
│   └── features.yaml      # Features group
└── scenarios/
    └── ...

File Structure

Each file in storage/ folder contains attribute groups. File name doesn't affect grouping — it's just way to logically organize configuration.

Example:

yaml
# storage/config.yaml - file name doesn't matter
settings:
  max_users: 100
  max_storage: 500
  default_language: "en"

limits:
  daily_requests: 1000
  max_file_size: 10485760

All files from storage/ folder combined during configuration synchronization.

Database Synchronization

Synchronization Mechanism

During Tenant Storage configuration synchronization system performs following actions:

  1. Get groups from DB — system gets list of all existing groups in database
  2. Determine groups for synchronization — compares groups from configuration with groups in DB
  3. Delete synchronized groups — deletes only groups present in configuration
  4. Load data from config — all groups from configuration loaded to database

Important Features

  • Groups from configuration — fully synchronized (old data deleted, new loaded)
  • Groups absent in config — remain untouched in database (not deleted automatically)
  • New groups — automatically created during synchronization
  • Full config match — synchronized groups fully match configuration

Example:

If DB has groups: settings, limits, features, old_group

And config has only: settings, limits, features

Then during synchronization:

  • settings, limits, features — will be deleted and reloaded from config
  • old_group — will remain untouched in DB (won't be deleted)

To delete group from DB use delete_storage action in scenario or manually delete all group records.

File Organization

Organization Principles

  1. Files for logical grouping — can create any number of files for convenient configuration organization
  2. File combining — all files from storage/ folder combined during synchronization
  3. Attribute groups — inside file use {group_key: {key: value}} structure to group related attributes
  4. Key uniqueness — keys must be unique within group (group_key)

Structure Example

storage/
├── config.yaml          # Arbitrary file name
│   settings:
│     max_users: 100
│     default_language: "en"
│   limits:
│     daily_requests: 1000
│     max_file_size: 10485760

└── additional.yaml     # Another file for additional settings
    features:
      enable_premium: true
      enable_api: false

Usage Example

File: storage/config.yaml (file name can be any)

yaml
# Basic settings
settings:
  max_users: 100                          # Integer
  max_storage: 500                        # Integer
  default_language: "en"                  # String
  allowed_languages: ["ru", "en", "de"]   # String array
  enable_notifications: true              # Boolean
  maintenance_mode: false                 # Boolean

# Limits and restrictions
limits:
  daily_requests: 1000                     # Integer
  max_file_size: 10485760                # Integer (10 MB in bytes)
  rate_limit_per_minute: 30              # Integer
  discount_percentage: 10.5               # Float
  price_tiers: [9.99, 19.99, 29.99]      # Float array

# Features and capabilities
features:
  enable_premium: true                    # Boolean
  enable_api: false                       # Boolean
  enable_webhooks: false                 # Boolean
  supported_regions: ["eu", "us", "asia"] # String array

# Tariffs and prices
pricing:
  base_price: 9.99                        # Float
  premium_price: 29.99                    # Float
  trial_days: 14                          # Integer

👤 User Storage

Purpose

User Storage — user data storage that allows:

  • Store user settings and preferences
  • Save user state between sessions
  • Manage personal user data
  • Isolate user data by tenants

Structure

User Storage has flat structure without groups:

{key: value}

Example:

yaml
language: "en"
theme: "dark"
messages_sent: 150
favorite_colors: ["red", "blue", "green"]
user_preferences:
  language: "en"
  theme: "dark"
  notifications: true

Features

  • Tenant isolation: User data tied to tenant_id and user_id
  • Flat structure: No grouping by group_key, only key: value
  • Programmatic management: Data set and changed only via scenario actions
  • Automatic context: tenant_id and user_id automatically available from event context

Usage Example

Setting values via scenario:

yaml
set_user_preferences:
  description: "Set user preferences"
  
  trigger:
    - event_type: "callback"
      callback_data: "set_preferences"
  
  step:
    - action: "set_user_storage"
      params:
        key: "language"
        value: "en"
    
    - action: "set_user_storage"
      params:
        key: "theme"
        value: "dark"
    
    - action: "set_user_storage"
      params:
        key: "user_preferences"
        value:
          language: "en"
          theme: "dark"
          notifications: true
    
    - action: "send_message"
      params:
        text: "✅ Settings saved!"

Getting values:

yaml
show_user_preferences:
  description: "Show user preferences"
  
  trigger:
    - event_type: "callback"
      callback_data: "show_preferences"
  
  step:
    # Get all values in one request
    - action: "get_user_storage"
    
    - action: "send_message"
      params:
        text: |
          👤 <b>Your settings</b>
          
          Language: <code>{_cache.user_storage_values.language|fallback:not set}</code>
          Theme: <code>{_cache.user_storage_values.theme|fallback:not set}</code>

Note: When requesting specific key (key specified) returns direct value available via {_cache.user_storage_values}. When requesting all values (without key) returns full structure {key: value} available via {_cache.user_storage_values.key}


🔢 Value Types

Supported Types

In both Storage types can use following data types:

  • Strings — text values
  • Integers — integer numbers
  • Floats — floating point numbers
  • Booleans — true/false
  • Arrays — value lists (support simple types and complex structures)
  • JSON objects (dictionaries) — nested data structures
  • Nested structures — dictionary arrays, dictionaries with arrays, nested dictionaries

How to Specify Values

In YAML files (for Tenant Storage) and when setting values (for User Storage) can write values in their natural form — system automatically determines type:

yaml
settings:
  max_users: 100                    # Integer
  price: 9.99                       # Float
  enable_feature: true              # Boolean
  default_language: "en"            # String
  allowed_languages: ["ru", "en", "de"]  # String array
  price_tiers: [9.99, 19.99, 29.99]      # Float array
  
  # JSON objects (dictionaries)
  admin_config:
    id: 1133574222
    name: "Admin"
    role: "admin"
    permissions: ["read", "write"]
  
  # Dictionary arrays (structure arrays)
  users:
    - id: 1
      name: "User 1"
      role: "user"
    - id: 2
      name: "User 2"
      role: "admin"
  
  # Nested structures
  nested_data:
    level1:
      level2:
        value: "deep nested"
        items: [1, 2, 3]

Boolean values:

  • true, false (boolean values in YAML)
  • "true", "false" (strings - will be converted to boolean values)

Arrays:

  • Can contain simple types: strings, numbers, float, bool
  • Can contain complex structures: dictionaries, nested arrays
  • Array elements automatically converted to appropriate types on read
  • Access to elements via placeholders: {_cache.storage_values.settings.allowed_languages[0]}

Dictionaries (JSON objects):

  • Can contain any supported value types
  • Support nesting up to 10 levels deep
  • Access to fields via dot notation: {_cache.storage_values.settings.admin_config.name}
  • Combination with arrays: {_cache.storage_values.settings.users[0].name}

🎬 Usage in Scenarios

Available Actions

Tenant Storage

In scenarios following actions available for working with Tenant Storage:

  • get_storage — get tenant storage values (one value, group or all values). Supports parameters group_key, key, group_key_pattern, key_pattern for flexible search
  • get_storage_groups — get list of unique group keys for tenant
  • set_storage — set values in tenant storage. Supports mixed approach: full structure via values or partial via group_key/key/value
  • delete_storage — delete values or groups from tenant storage. Supports deleting specific value, group or by patterns

User Storage

In scenarios following actions available for working with User Storage:

  • get_user_storage — get user storage values (one value or all values). Supports parameters key and key_pattern for flexible search
  • set_user_storage — set values in user storage. Supports mixed approach: full structure via values or partial via key/value
  • delete_user_storage — delete values from user storage. Supports deleting specific value, all values or by pattern

Important:

  • Parameters tenant_id and user_id automatically available from event context and don't require explicit specification in scenarios
  • Data from action response_data automatically saved to _cache (flat caching by default)
  • Access to data via placeholders: {_cache.storage_values} for Tenant Storage and {_cache.user_storage_values} for User Storage
  • For Tenant Storage when requesting only group (group_key specified, key not specified): returns group structure {key: value} (without group_key nesting), available via {_cache.storage_values.{key}}
  • For Tenant Storage when requesting specific key (group_key and key specified): returns direct value available via {_cache.storage_values}
  • For Tenant Storage when requesting all values (nothing specified): returns full structure {group_key: {key: value}} available via {_cache.storage_values.{group_key}.{key}}
  • For User Storage when requesting all values: {_cache.user_storage_values.{key}}
  • For User Storage when requesting specific key: {_cache.user_storage_values} (direct value)

Example 1: Getting and Displaying Value

Simple example — getting one value:

yaml
show_settings:
  description: "Show tenant settings"
  
  trigger:
    - event_type: "message"
      event_text: "/settings"
  
  step:
    # Get value from storage
    - action: "get_storage"
      params:
        group_key: "settings"
        key: "max_users"
    
    # Use obtained value in message
    - action: "send_message"
      params:
        text: |
          📊 <b>Settings</b>
          
          Max users: <code>{_cache.storage_values}</code>

Value access:

  • When requesting specific key (group_key and key specified) returns direct value (primitive or object as is) available via {_cache.storage_values}
  • In example above: {_cache.storage_values} returns value 100 (or other max_users value)
  • When requesting only group (group_key specified, key not specified) returns group structure {key: value} available via {_cache.storage_values.{key}}

Example 2: Getting Group Values

Getting all group values in one request:

yaml
show_all_settings:
  description: "Show all settings"
  
  trigger:
    - event_type: "callback"
      callback_data: "show_settings"
  
  step:
    # Get entire settings group in one request
    - action: "get_storage"
      params:
        group_key: "settings"
    
    # Use all group values
    - action: "send_message"
      params:
        text: |
          ⚙️ <b>All settings</b>
          
          Max users: <code>{_cache.storage_values.max_users}</code>
          Max storage: <code>{_cache.storage_values.max_storage}</code>
          Default language: <code>{_cache.storage_values.default_language}</code>
          
          Notifications: {_cache.storage_values.enable_notifications|true|value:✅|fallback:❌}

Advantages:

  • One request instead of multiple separate requests
  • All group values loaded at once
  • More efficient for displaying group of related settings
  • When requesting only group_key returns group structure {key: value} (without group_key nesting) available via {_cache.storage_values.{key}}

Example 3: Working with User Storage

Getting and setting user data:

yaml
# Setting user preferences
set_user_settings:
  description: "Set user preferences"
  
  trigger:
    - event_type: "callback"
      callback_data: "set_settings"
  
  step:
    - action: "set_user_storage"
      params:
        key: "language"
        value: "en"
    
    - action: "set_user_storage"
      params:
        key: "theme"
        value: "dark"
    
    - action: "set_user_storage"
      params:
        key: "messages_sent"
        value: 0
    
    - action: "send_message"
      params:
        text: "✅ Settings saved!"

# Getting all user data
show_user_data:
  description: "Show all user data"
  
  trigger:
    - event_type: "callback"
      callback_data: "show_data"
  
  step:
    - action: "get_user_storage"
    
    - action: "send_message"
      params:
        text: |
          👤 <b>Your data</b>
          
          Language: <code>{_cache.user_storage_values.language|fallback:not set}</code>
          Theme: <code>{_cache.user_storage_values.theme|fallback:not set}</code>
          Messages sent: <code>{_cache.user_storage_values.messages_sent|fallback:0}</code>

# Deleting user data
delete_user_data:
  description: "Delete user data"
  
  trigger:
    - event_type: "callback"
      callback_data: "delete_data"
  
  step:
    - action: "delete_user_storage"
      params:
        key: "language"
    
    - action: "send_message"
      params:
        text: "🗑️ Data deleted!"

User Storage features:

  • tenant_id and user_id automatically available from event context
  • Data isolated by tenants and users
  • No grouping — flat structure {key: value}
  • Data set only via scenario actions

Example 4: YAML Formatting

Displaying data in readable YAML format:

yaml
show_formatted_storage:
  description: "Show storage in YAML format"
  
  trigger:
    - event_type: "callback"
      callback_data: "show_formatted"
  
  step:
    # Getting group with formatting
    - action: "get_storage"
      params:
        group_key: "settings"
        format: true
    
    - action: "send_message"
      params:
        text: |
          ⚙️ <b>Settings (YAML format)</b>
          
          <pre>{_cache.formatted_text}</pre>

format parameter:

  • Available for all data getting actions (get_storage, get_user_storage)
  • When format: true field formatted_text added to response_data with data in YAML format
  • Access to formatted text via {_cache.formatted_text}
  • Convenient for displaying complex data structures to users

Coreness — Create. Automate. Scale.