chore(api): Use uuidv7 as PK for new provider crendential tables (#24545)

This commit is contained in:
QuantumGhost
2025-08-27 11:22:08 +08:00
committed by GitHub
parent 726c429772
commit 58189ed9a0
4 changed files with 81 additions and 25 deletions

View File

@@ -6,10 +6,10 @@ Create Date: 2025-08-09 15:53:54.341341
""" """
from alembic import op from alembic import op
from libs.uuid_utils import uuidv7
import models as models import models as models
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.sql import table, column from sqlalchemy.sql import table, column
import uuid
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = 'e8446f481c1e' revision = 'e8446f481c1e'
@@ -21,7 +21,7 @@ depends_on = None
def upgrade(): def upgrade():
# Create provider_credentials table # Create provider_credentials table
op.create_table('provider_credentials', op.create_table('provider_credentials',
sa.Column('id', models.types.StringUUID(), server_default=sa.text('uuid_generate_v4()'), nullable=False), sa.Column('id', models.types.StringUUID(), server_default=sa.text('uuidv7()'), nullable=False),
sa.Column('tenant_id', models.types.StringUUID(), nullable=False), sa.Column('tenant_id', models.types.StringUUID(), nullable=False),
sa.Column('provider_name', sa.String(length=255), nullable=False), sa.Column('provider_name', sa.String(length=255), nullable=False),
sa.Column('credential_name', sa.String(length=255), nullable=False), sa.Column('credential_name', sa.String(length=255), nullable=False),
@@ -87,7 +87,7 @@ def migrate_existing_providers_data():
# Iterate through each provider and insert into provider_credentials # Iterate through each provider and insert into provider_credentials
for provider in existing_providers: for provider in existing_providers:
credential_id = str(uuid.uuid4()) credential_id = str(uuidv7())
if not provider.encrypted_config or provider.encrypted_config.strip() == '': if not provider.encrypted_config or provider.encrypted_config.strip() == '':
continue continue

View File

@@ -5,9 +5,9 @@ Revises: e8446f481c1e
Create Date: 2025-08-13 16:05:42.657730 Create Date: 2025-08-13 16:05:42.657730
""" """
import uuid
from alembic import op from alembic import op
from libs.uuid_utils import uuidv7
import models as models import models as models
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.sql import table, column from sqlalchemy.sql import table, column
@@ -23,7 +23,7 @@ depends_on = None
def upgrade(): def upgrade():
# Create provider_model_credentials table # Create provider_model_credentials table
op.create_table('provider_model_credentials', op.create_table('provider_model_credentials',
sa.Column('id', models.types.StringUUID(), server_default=sa.text('uuid_generate_v4()'), nullable=False), sa.Column('id', models.types.StringUUID(), server_default=sa.text('uuidv7()'), nullable=False),
sa.Column('tenant_id', models.types.StringUUID(), nullable=False), sa.Column('tenant_id', models.types.StringUUID(), nullable=False),
sa.Column('provider_name', sa.String(length=255), nullable=False), sa.Column('provider_name', sa.String(length=255), nullable=False),
sa.Column('model_name', sa.String(length=255), nullable=False), sa.Column('model_name', sa.String(length=255), nullable=False),
@@ -102,7 +102,7 @@ def migrate_existing_provider_models_data():
if not provider_model.encrypted_config or provider_model.encrypted_config.strip() == '': if not provider_model.encrypted_config or provider_model.encrypted_config.strip() == '':
continue continue
credential_id = str(uuid.uuid4()) credential_id = str(uuidv7())
# Insert into provider_model_credentials table # Insert into provider_model_credentials table
conn.execute( conn.execute(

View File

@@ -274,7 +274,7 @@ class ProviderCredential(Base):
sa.Index("provider_credential_tenant_provider_idx", "tenant_id", "provider_name"), sa.Index("provider_credential_tenant_provider_idx", "tenant_id", "provider_name"),
) )
id: Mapped[str] = mapped_column(StringUUID, server_default=text("uuid_generate_v4()")) id: Mapped[str] = mapped_column(StringUUID, server_default=text("uuidv7()"))
tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False) tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
provider_name: Mapped[str] = mapped_column(String(255), nullable=False) provider_name: Mapped[str] = mapped_column(String(255), nullable=False)
credential_name: Mapped[str] = mapped_column(String(255), nullable=False) credential_name: Mapped[str] = mapped_column(String(255), nullable=False)
@@ -300,7 +300,7 @@ class ProviderModelCredential(Base):
), ),
) )
id: Mapped[str] = mapped_column(StringUUID, server_default=text("uuid_generate_v4()")) id: Mapped[str] = mapped_column(StringUUID, server_default=text("uuidv7()"))
tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False) tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
provider_name: Mapped[str] = mapped_column(String(255), nullable=False) provider_name: Mapped[str] = mapped_column(String(255), nullable=False)
model_name: Mapped[str] = mapped_column(String(255), nullable=False) model_name: Mapped[str] = mapped_column(String(255), nullable=False)

View File

@@ -10,11 +10,13 @@ more reliable and realistic test scenarios.
import logging import logging
import os import os
from collections.abc import Generator from collections.abc import Generator
from pathlib import Path
from typing import Optional from typing import Optional
import pytest import pytest
from flask import Flask from flask import Flask
from flask.testing import FlaskClient from flask.testing import FlaskClient
from sqlalchemy import Engine, text
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from testcontainers.core.container import DockerContainer from testcontainers.core.container import DockerContainer
from testcontainers.core.waiting_utils import wait_for_logs from testcontainers.core.waiting_utils import wait_for_logs
@@ -64,7 +66,7 @@ class DifyTestContainers:
# PostgreSQL is used for storing user data, workflows, and application state # PostgreSQL is used for storing user data, workflows, and application state
logger.info("Initializing PostgreSQL container...") logger.info("Initializing PostgreSQL container...")
self.postgres = PostgresContainer( self.postgres = PostgresContainer(
image="postgres:16-alpine", image="postgres:14-alpine",
) )
self.postgres.start() self.postgres.start()
db_host = self.postgres.get_container_host_ip() db_host = self.postgres.get_container_host_ip()
@@ -116,7 +118,7 @@ class DifyTestContainers:
# Start Redis container for caching and session management # Start Redis container for caching and session management
# Redis is used for storing session data, cache entries, and temporary data # Redis is used for storing session data, cache entries, and temporary data
logger.info("Initializing Redis container...") logger.info("Initializing Redis container...")
self.redis = RedisContainer(image="redis:latest", port=6379) self.redis = RedisContainer(image="redis:6-alpine", port=6379)
self.redis.start() self.redis.start()
redis_host = self.redis.get_container_host_ip() redis_host = self.redis.get_container_host_ip()
redis_port = self.redis.get_exposed_port(6379) redis_port = self.redis.get_exposed_port(6379)
@@ -184,6 +186,57 @@ class DifyTestContainers:
_container_manager = DifyTestContainers() _container_manager = DifyTestContainers()
def _get_migration_dir() -> Path:
conftest_dir = Path(__file__).parent
return conftest_dir.parent.parent / "migrations"
def _get_engine_url(engine: Engine):
try:
return engine.url.render_as_string(hide_password=False).replace("%", "%%")
except AttributeError:
return str(engine.url).replace("%", "%%")
_UUIDv7SQL = r"""
/* Main function to generate a uuidv7 value with millisecond precision */
CREATE FUNCTION uuidv7() RETURNS uuid
AS
$$
-- Replace the first 48 bits of a uuidv4 with the current
-- number of milliseconds since 1970-01-01 UTC
-- and set the "ver" field to 7 by setting additional bits
SELECT encode(
set_bit(
set_bit(
overlay(uuid_send(gen_random_uuid()) placing
substring(int8send((extract(epoch from clock_timestamp()) * 1000)::bigint) from
3)
from 1 for 6),
52, 1),
53, 1), 'hex')::uuid;
$$ LANGUAGE SQL VOLATILE PARALLEL SAFE;
COMMENT ON FUNCTION uuidv7 IS
'Generate a uuid-v7 value with a 48-bit timestamp (millisecond precision) and 74 bits of randomness';
CREATE FUNCTION uuidv7_boundary(timestamptz) RETURNS uuid
AS
$$
/* uuid fields: version=0b0111, variant=0b10 */
SELECT encode(
overlay('\x00000000000070008000000000000000'::bytea
placing substring(int8send(floor(extract(epoch from $1) * 1000)::bigint) from 3)
from 1 for 6),
'hex')::uuid;
$$ LANGUAGE SQL STABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION uuidv7_boundary(timestamptz) IS
'Generate a non-random uuidv7 with the given timestamp (first 48 bits) and all random bits to 0.
As the smallest possible uuidv7 for that timestamp, it may be used as a boundary for partitions.';
"""
def _create_app_with_containers() -> Flask: def _create_app_with_containers() -> Flask:
""" """
Create Flask application configured to use test containers. Create Flask application configured to use test containers.
@@ -211,7 +264,10 @@ def _create_app_with_containers() -> Flask:
# Initialize database schema # Initialize database schema
logger.info("Creating database schema...") logger.info("Creating database schema...")
with app.app_context(): with app.app_context():
with db.engine.connect() as conn, conn.begin():
conn.execute(text(_UUIDv7SQL))
db.create_all() db.create_all()
logger.info("Database schema created successfully") logger.info("Database schema created successfully")