Error en la nueva función de flujos de trabajo

Hola, no tengo Workflows habilitado, pero parece que está llenando mi registro de entradas.

Discourse | v2026.7.0-latest +394

Excepción de trabajo: PG::UndefinedTable: ERROR: la relación “discourse_workflows_webhooks” no existe LÍNEA 10: WHERE a.attrelid = ‘“discourse_workflows_webhooks”’::regcla…

Mensaje (252 copias reportadas)

Excepción de trabajo: PG::UndefinedTable: ERROR: la relación “discourse_workflows_webhooks” no existe
LÍNEA 10: WHERE a.attrelid = ‘“discourse_workflows_webhooks”’::regcla…

^

Pila de llamadas

rack-mini-profiler-4.0.1/lib/patches/db/pg/alias_method.rb:109:in 'PG::Connection#exec'
rack-mini-profiler-4.0.1/lib/patches/db/pg/alias_method.rb:109:in 'PG::Connection#async_exec'
activerecord-8.0.5/lib/active_record/connection_adapters/postgresql/database_statements.rb:167:in 'ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements#perform_query'
activerecord-8.0.5/lib/active_record/connection_adapters/abstract/database_statements.rb:556:in 'block (2 levels) in ActiveRecord::ConnectionAdapters::DatabaseStatements#raw_execute'
activerecord-8.0.5/lib/active_record/connection_adapters/abstract_adapter.rb:1022:in 'block in ActiveRecord::ConnectionAdapters::AbstractAdapter#with_raw_connection'
activesupport-8.0.5/lib/active_support/concurrency/null_lock.rb:9:in 'ActiveSupport::Concurrency::NullLock#synchronize'
activerecord-8.0.5/lib/active_record/connection_adapters/abstract_adapter.rb:991:in 'ActiveRecord::ConnectionAdapters::AbstractAdapter#with_raw_connection'
activerecord-8.0.5/lib/active_record/connection_adapters/abstract/database_statements.rb:555:in 'block in ActiveRecord::ConnectionAdapters::DatabaseStatements#raw_execute'
activesupport-8.0.5/lib/active_support/notifications/instrumenter.rb:58:in 'ActiveSupport::Notifications::Instrumenter#instrument'
activerecord-8.0.5/lib/active_record/connection_adapters/abstract_adapter.rb:1142:in 'ActiveRecord::ConnectionAdapters::AbstractAdapter#log'
activerecord-8.0.5/lib/active_record/connection_adapters/abstract/database_statements.rb:554:in 'ActiveRecord::ConnectionAdapters::DatabaseStatements#raw_execute'
activerecord-8.0.5/lib/active_record/connection_adapters/abstract/database_statements.rb:591:in 'ActiveRecord::ConnectionAdapters::DatabaseStatements#internal_execute'
activerecord-8.0.5/lib/active_record/connection_adapters/postgresql/database_statements.rb:15:in 'ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements#query'
activerecord-8.0.5/lib/active_record/connection_adapters/postgresql_adapter.rb:1042:in 'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter#column_definitions'
activerecord-8.0.5/lib/active_record/connection_adapters/abstract/schema_statements.rb:109:in 'ActiveRecord::ConnectionAdapters::SchemaStatements#columns'
activerecord-8.0.5/lib/active_record/connection_adapters/schema_cache.rb:345:in 'block (2 levels) in ActiveRecord::ConnectionAdapters::SchemaCache#columns'
activerecord-8.0.5/lib/active_record/connection_adapters/abstract/connection_pool.rb:416:in 'ActiveRecord::ConnectionAdapters::ConnectionPool#with_connection'
activerecord-8.0.5/lib/active_record/connection_adapters/schema_cache.rb:344:in 'block in ActiveRecord::ConnectionAdapters::SchemaCache#columns'
activerecord-8.0.5/lib/active_record/connection_adapters/schema_cache.rb:343:in 'Hash#fetch'
activerecord-8.0.5/lib/active_record/connection_adapters/schema_cache.rb:343:in 'ActiveRecord::ConnectionAdapters::SchemaCache#columns'
activerecord-8.0.5/lib/active_record/connection_adapters/schema_cache.rb:354:in 'block in ActiveRecord::ConnectionAdapters::SchemaCache#columns_hash'
activerecord-8.0.5/lib/active_record/connection_adapters/schema_cache.rb:353:in 'Hash#fetch'
activerecord-8.0.5/lib/active_record/connection_adapters/schema_cache.rb:353:in 'ActiveRecord::ConnectionAdapters::SchemaCache#columns_hash'
activerecord-8.0.5/lib/active_record/connection_adapters/schema_cache.rb:54:in 'ActiveRecord::ConnectionAdapters::SchemaReflection#columns_hash'
activerecord-8.0.5/lib/active_record/connection_adapters/schema_cache.rb:198:in 'ActiveRecord::ConnectionAdapters::BoundSchemaReflection#columns_hash'
activerecord-8.0.5/lib/active_record/model_schema.rb:592:in 'ActiveRecord::ModelSchema::ClassMethods#load_schema!'
activerecord-8.0.5/lib/active_record/counter_cache.rb:187:in 'ActiveRecord::CounterCache::ClassMethods#load_schema!'
activerecord-8.0.5/lib/active_record/encryption/encryptable_record.rb:127:in 'ActiveRecord::Encryption::EncryptableRecord::ClassMethods#load_schema!'
activerecord-8.0.5/lib/active_record/model_schema.rb:539:in 'block in ActiveRecord::ModelSchema::ClassMethods#load_schema'
activerecord-8.0.5/lib/active_record/model_schema.rb:536:in 'Monitor#synchronize'
activerecord-8.0.5/lib/active_record/model_schema.rb:536:in 'ActiveRecord::ModelSchema::ClassMethods#load_schema'
activerecord-8.0.5/lib/active_record/model_schema.rb:428:in 'ActiveRecord::ModelSchema::ClassMethods#columns_hash'
activerecord-8.0.5/lib/active_record/attributes.rb:255:in 'block in ActiveRecord::Attributes::ClassMethods#_default_attributes'
activerecord-8.0.5/lib/active_record/connection_adapters/abstract/connection_pool.rb:422:in 'ActiveRecord::ConnectionAdapters::ConnectionPool#with_connection'
activerecord-8.0.5/lib/active_record/connection_handling.rb:312:in 'ActiveRecord::ConnectionHandling#with_connection'
activerecord-8.0.5/lib/active_record/attributes.rb:254:in 'ActiveRecord::Attributes::ClassMethods#_default_attributes'
activemodel-8.0.5/lib/active_model/attribute_registration.rb:38:in 'ActiveModel::AttributeRegistration::ClassMethods#attribute_types'
activemodel-8.0.5/lib/active_model/attribute_registration.rb:49:in 'ActiveModel::AttributeRegistration::ClassMethods#type_for_attribute'
activerecord-8.0.5/lib/active_record/type_caster/map.rb:16:in 'ActiveRecord::TypeCaster::Map#type_for_attribute'
activerecord-8.0.5/lib/arel/table.rb:107:in 'Arel::Table#type_for_attribute'
activerecord-8.0.5/lib/active_record/table_metadata.rb:18:in 'ActiveRecord::TableMetadata#type'
activerecord-8.0.5/lib/active_record/relation/predicate_builder.rb:59:in 'ActiveRecord::PredicateBuilder#build'
activerecord-8.0.5/lib/active_record/relation/predicate_builder.rb:54:in 'ActiveRecord::PredicateBuilder#
'
activerecord-8.0.5/lib/active_record/relation/predicate_builder.rb:145:in 'block in ActiveRecord::PredicateBuilder#expand_from_hash'
activerecord-8.0.5/lib/active_record/relation/predicate_builder.rb:87:in 'Hash#each'
activerecord-8.0.5/lib/active_record/relation/predicate_builder.rb:87:in 'Enumerable#flat_map'
activerecord-8.0.5/lib/active_record/relation/predicate_builder.rb:87:in 'ActiveRecord::PredicateBuilder#expand_from_hash'
activerecord-8.0.5/lib/active_record/relation/predicate_builder.rb:25:in 'ActiveRecord::PredicateBuilder#build_from_hash'
activerecord-8.0.5/lib/active_record/relation/query_methods.rb:1643:in 'ActiveRecord::QueryMethods#build_where_clause'
activerecord-8.0.5/lib/active_record/relation/query_methods.rb:1044:in 'ActiveRecord::QueryMethods#where!'
activerecord-8.0.5/lib/active_record/relation/query_methods.rb:1039:in 'ActiveRecord::QueryMethods#where'
/var/www/discourse/plugins/discourse-workflows/app/models/discourse_workflows/webhook.rb:12:in 'block in class:Webhook'
activerecord-8.0.5/lib/active_record/relation.rb:565:in 'BasicObject#instance_exec'
activerecord-8.0.5/lib/active_record/relation.rb:565:in 'block in ActiveRecord::Relation#_exec_scope'
activerecord-8.0.5/lib/active_record/relation.rb:1383:in 'ActiveRecord::Relation#_scoping'
activerecord-8.0.5/lib/active_record/relation.rb:565:in 'ActiveRecord::Relation#_exec_scope'
activerecord-8.0.5/lib/active_record/scoping/named.rb:175:in 'block in DiscourseWorkflows::Webhook.scope'
/var/www/discourse/plugins/discourse-workflows/lib/discourse_workflows/webhook_test_listener.rb:88:in 'DiscourseWorkflows::WebhookTestListener.purge_expired!'
/var/www/discourse/plugins/discourse-workflows/app/jobs/scheduled/discourse_workflows/purge_expired_webhook_test_listeners.rb:9:in 'Jobs::DiscourseWorkflows::PurgeExpiredWebhookTestListeners#execute'
/var/www/discourse/app/jobs/base.rb:313:in 'block (2 levels) in Jobs::Base#perform'
rails_multisite-7.0.0/lib/rails_multisite/connection_management/null_instance.rb:49:in 'RailsMultisite::ConnectionManagement::NullInstance#with_connection'
rails_multisite-7.0.0/lib/rails_multisite/connection_management.rb:17:in 'RailsMultisite::ConnectionManagement.with_connection'
/var/www/discourse/app/jobs/base.rb:300:in 'block in Jobs::Base#perform'
/var/www/discourse/app/jobs/base.rb:297:in 'Array#each'
/var/www/discourse/app/jobs/base.rb:297:in 'Jobs::Base#perform'
/var/www/discourse/app/jobs/base.rb:375:in 'Jobs::Scheduled#perform'
mini_scheduler-0.20.0/lib/mini_scheduler/manager.rb:137:in 'MiniScheduler::Manager::Runner#process_queue'
mini_scheduler-0.20.0/lib/mini_scheduler/manager.rb:77:in 'MiniScheduler::Manager::Runner#worker_loop'
mini_scheduler-0.20.0/lib/mini_scheduler/manager.rb:63:in 'block (2 levels) in MiniScheduler::Manager::Runner#ensure_worker_threads'




Parece que tienes el complemento instalado, pero no has ejecutado las migraciones para crear las tablas necesarias que utiliza.

¿Puedes mostrarnos la parte de tu definición de contenedor donde lo instalas?

Gracias, pero discourse-workflows no es algo que haya instalado; es un plugin central incluido (se distribuye en plugins/, creado por Discourse, detrás de enable_discourse_workflows), por lo que no hay nada en mi app.yml para ello.

La tabla tiene una línea de migración en la línea 212 de db/migrate/20260312000000_create_workflow_tables.rb que crea discourse_workflows_webhooks, pero nunca se ejecutó en mi base de datos.

Publico esto principalmente por si vale la pena añadir una comprobación en ese trabajo programado (PurgeExpiredWebhookTestListeners) para que una tabla faltante degrade silenciosamente en lugar de generar un error en cada ejecución.

1 me gusta

La tabla discourse_workflows_webhooks sí tiene una migración que la crea (create_workflow_tables, versión 20260312000000, el create_table está cerca del final de ese archivo).

En mi base de datos, esa migración ya está registrada como ejecutada en schema_migrations, pero la tabla de webhooks nunca fue creada.

Un db:migrate limpio lo confirmó: se ejecutó sin salida, por lo que no había nada pendiente, y sin embargo la tabla seguía sin existir. Rails utiliza el número de versión, ve la migración como completada y nunca la ejecuta de nuevo, por lo que la tabla permanece ausente mientras el modelo y el trabajo programado de purga siguen cargándose y ejecutándose. Esa es la razón por la que la misma excepción del trabajo seguía acumulándose.

La corrección que apliqué: Dado que la migración está registrada, volver a ejecutarla no era una opción, porque intentaría recrear las otras tablas de flujo de trabajo que ya existen y fallaría.

Así que creé la tabla faltante directamente, coincidiendo exactamente con la migración (mismas columnas, mismos cinco índices, incluido el único):

conn = ActiveRecord::Base.connection
unless conn.table_exists?(:discourse_workflows_webhooks)
conn.create_table :discourse_workflows_webhooks do |t|
t.bigint :workflow_id, null: false
t.string :workflow_version_id, limit: 36
t.string :node_name, null: false, limit: 100
t.string :webhook_path, null: false, limit: 500
t.string :http_method, null: false, limit: 10
t.string :webhook_id, limit: 36
t.integer :path_length
t.boolean :test_webhook, null: false, default: false
t.integer :user_id
t.jsonb :workflow_snapshot
t.datetime :expires_at
t.datetime :created_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
end
conn.add_index :discourse_workflows_webhooks, %i[http_method webhook_path test_webhook], unique: true, name: "idx_dwf_webhooks_on_method_path_test"
conn.add_index :discourse_workflows_webhooks, %i[webhook_id http_method test_webhook], name: "idx_dwf_webhooks_on_webhook_id_method_test", where: "webhook_id IS NOT NULL"
conn.add_index :discourse_workflows_webhooks, :workflow_id, name: "idx_dwf_webhooks_on_workflow_id"
conn.add_index :discourse_workflows_webhooks, :workflow_version_id, name: "idx_dwf_webhooks_on_workflow_version_id"
conn.add_index :discourse_workflows_webhooks, :expires_at, name: "idx_dwf_webhooks_on_expires_at", where: "expires_at IS NOT NULL"
end

Después de eso, table_exists? devuelve true y el trabajo PurgeExpiredWebhookTestListeners deja de generar errores en su próxima ejecución.

Dado que esto se debió a una migración publicada que fue editada después de haberse ejecutado, una actualización posterior del núcleo que agregue una migración adecuada para esta tabla podría intentar crearla de nuevo y fallar con “ya existe”.

Si eso ocurre después de una actualización, la solución es dejar que la nueva migración prevalezca: eliminar primero la tabla que creé a mano, o insertar su versión en schema_migrations para que se omita.

Lo señalo por si alguien más en la rama más reciente se encuentra con la misma excepción de trabajo.

1 me gusta