Introducing DBIx::Class::Schema::SanityChecker
authorPeter Rabbitson <ribasushi@cpan.org>
Mon, 23 May 2016 09:08:17 +0000 (11:08 +0200)
committerPeter Rabbitson <ribasushi@cpan.org>
Tue, 26 Jul 2016 09:19:28 +0000 (11:19 +0200)
commit12e7015aa9372aeaf1aaa7e125b8ac8da216deb5
tree7513157b1b22bad90d9fad34f9d0724f04a01b4a
parent1b822bd3e15476666e97d9a95754f123410b3c56
Introducing DBIx::Class::Schema::SanityChecker

This gives us comprehensive diagnostic on incorrect component composition
and other hard to track... stuff.

Given the huge amount of changes to call chains (specifically the changes
in 77c3a5dc and e5053694), and the fallout seen on CPAN and darkpan due to
these modifications, the status quo became clearly untennable. To mitigate
the (often silent) breakage a brand new "sanity check" framework was
introduced as part of the ::Schema setup-cycle (and is enabled by default)

Same DBIx::Class::Helper v2.032002 test time shoots from 65.5s all the way
to 76.0s, a 16% slowdown. However the moment the framework is disabled by
flipping $schema->schema_sanity_checker to a defined-but-false value - the
startup impact is entirely gone.

The changset was extensively tested against the following set of downstream
dists (a superset of c8b1011e), with each warning hand-confirmed to be a
valid description of a real problem:

--- actual bash script passing on a *heavily* massaged PERL5LIB

set -o pipefail

export PERL_CPANM_OPT=
export PERL5LIB="/home/rabbit/devel/dbic/dbgit/lib:$PERL5LIB"
export DBICTEST_SQLT_DEPLOY=0
export DBIC_ASSERT_NO_ERRONEOUS_METAINSTANCE_USE=1
export DBIC_ASSERT_NO_FAILING_SANITY_CHECKS=1

# these fail with ERRONEOUS_METAINSTANCE_USE alone
# (S::L fails due to PG_DSN but I think is ok besides that)
for d in \
    DBICx::Shortcuts \
    DBIx::Class::Bootstrap::Simple \
    DBIx::Class::Preview \
    DBIx::Class::Schema::Loader \
    Pinto \
; do \
  DBIC_ASSERT_NO_ERRONEOUS_METAINSTANCE_USE=0 \
  DBIC_ASSERT_NO_FAILING_SANITY_CHECKS=0 \
  DBICTEST_PG_DSN= \
  cpanm -v --reinstall $d 2>&1 \
| tee -a /dev/shm/umpfh \
| grep -P -B1 'sanity check|emit_|^(Building and testing|Result:)' || exit 1 \
; done

# these emit various san-check related problems
for d in \
    RapidApp \
    Data::OFAC \
    DBIx::Class::VirtualColumns \
    "DBD::SQLite@1.35 Handel" \
    DBIx::Class::RDBOHelpers \
    CatalystX::CRUD::ModelAdapter::DBIC \
    DBICx::Indexing \
    DBICx::TestDatabase \
    DBIx::Class::BitField \
    DBIx::Class::I18NColumns \
    DBIx::Class::PhoneticSearch \
    DBIx::Class::RandomColumns \
    DBIx::Class::ResultSource::MultipleTableInheritance \
    DBIx::Class::Result::ProxyField \
    DBIx::Class::Schema::PopulateMore \
    DBIx::Class::Tree \
    Foorum \
    Interchange6::Schema \
    Test::DBIx::Class \
    TreePath \
; do \
  DBIC_ASSERT_NO_FAILING_SANITY_CHECKS=0 \
  cpanm -v --reinstall $d 2>&1 \
| tee -a /dev/shm/umpfh \
| grep -P -B1 'sanity check|emit_|^(Building and testing|Result:)' || exit 1 \
; done

# these are entirely unaffected \o/
for d in \
    Dancer2::Plugin::DBIC \
    App::DBCritic \
    App::DH \
    AproJo \
    Articulate \
    Authorization::RBAC \
    BackPAN::Index \
    Bio::Chado::Schema \
    Bot::BasicBot::Pluggable::Module::Notes \
    Bracket \
    Business::Cart::Generic \
    Business::DPD \
    Catalyst::Authentication::Credential::Facebook \
    Catalyst::Authentication::Store::DBIx::Class \
    Catalyst::Controller::DBIC::API \
    Catalyst::Model::DBIC::Plain \
    Catalyst::Model::DBIC::Schema \
    Catalyst::Model::DBIC::Schema::PerRequest \
    Catalyst::Model::FormFu \
    Catalyst::Plugin::Authentication::Store::DBIC \
    Catalyst::Plugin::Authorization::Abilities \
    Catalyst::Plugin::AutoCRUD \
    Catalyst::Plugin::DBIC::Schema::Profiler \
    Catalyst::Plugin::Session::Store::DBIC \
    Catalyst::TraitFor::Controller::DBIC::DoesPaging \
    Catalyst::TraitFor::Model::DBIC::Schema::RequestConnectionPool \
    Catalyst::TraitFor::Model::DBIC::Schema::Result \
    Catalyst::TraitFor::Model::DBIC::Schema::WithCurrentUser \
    Catalyst::View::CSV \
    CatalystX::Controller::ExtJS::REST::SimpleExcel \
    CatalystX::Crudite \
    CatalystX::Eta \
    CatalystX::OAuth2 \
    CatalystX::Resource \
    CGI::Application::Plugin::Authentication::Driver::DBIC \
    CGI::Application::Plugin::DBIC::Schema \
    CGI::Application::Plugin::DBIx::Class \
    CGI::Application::Plugin::ExtJS \
    CGI::Session::Driver::dbic \
    Cookieville \
    Dancer2::Plugin::Auth::Extensible::Provider::DBIC \
    Dancer2::Session::DBIC \
    Dancer::Plugin::Auth::Extensible::Provider::DBIC \
    Dancer::Plugin::Auth::RBAC::Credentials::DBIC \
    Dancer::Plugin::Auth::RBAC::Permissions::DBIC \
    Dancer::Plugin::DBIC \
    Dancer::Session::DBIC \
    Data::Morph \
    DBICx::AutoDoc \
    DBICx::Backend::Move \
    DBICx::DataDictionary \
    DBICx::Deploy \
    DBICx::Hooks \
    DBICx::MapMaker \
    DBICx::MaterializedPath \
    DBICx::Modeler \
    DBICx::Sugar \
    DBICx::TxnInsert \
    DBIx::Class::AlwaysUpdate \
    DBIx::Class::AuditAny \
    DBIx::Class::AuditLog \
    DBIx::Class::BatchUpdate \
    DBIx::Class::Candy \
    DBIx::Class::ColumnDefault \
    DBIx::Class::CompressColumns \
    DBIx::Class::Cursor::Cached \
    DBIx::Class::CustomPrefetch \
    DBIx::Class::DateTime::Epoch \
    DBIx::Class::DeleteAction \
    DBIx::Class::DeploymentHandler \
    DBIx::Class::DigestColumns \
    DBIx::Class::DynamicDefault \
    DBIx::Class::DynamicSubclass \
    DBIx::Class::EasyFixture \
    DBIx::Class::ElasticSync \
    DBIx::Class::EncodeColumns \
    DBIx::Class::EncodedColumn \
    DBIx::Class::Factory \
    DBIx::Class::Fixtures \
    DBIx::Class::ForceUTF8 \
    DBIx::Class::FormatColumns \
    DBIx::Class::FormTools \
    DBIx::Class::FromSledge \
    DBIx::Class::FrozenColumns \
    DBIx::Class::GeomColumns \
    DBIx::Class::Graph \
    DBIx::Class::Helpers \
    DBIx::Class::HTML::FormFu \
    DBIx::Class::HTMLWidget \
    DBIx::Class::Indexed \
    DBIx::Class::InflateColumn::Authen::Passphrase \
    DBIx::Class::InflateColumn::BigFloat \
    DBIx::Class::InflateColumn::Boolean \
    DBIx::Class::InflateColumn::Currency \
    DBIx::Class::InflateColumn::DateTime::Duration \
    DBIx::Class::InflateColumn::DateTime::WithTimeZone \
    DBIx::Class::InflateColumn::DateTimeX::Immutable \
    DBIx::Class::InflateColumn::FS \
    DBIx::Class::InflateColumn::IP \
    DBIx::Class::InflateColumn::Markup::Unified \
    DBIx::Class::InflateColumn::Math::Currency \
    DBIx::Class::InflateColumn::Object::Enum \
    DBIx::Class::InflateColumn::Path::Class \
    DBIx::Class::InflateColumn::Serializer \
    DBIx::Class::InflateColumn::Serializer::JSYNC \
    DBIx::Class::InflateColumn::Serializer::Role::HashContentAccessor \
    DBIx::Class::InflateColumn::Serializer::Sereal \
    DBIx::Class::InflateColumn::Time \
    DBIx::Class::InflateColumn::TimeMoment \
    DBIx::Class::InflateColumn::URI \
    DBIx::Class::IntrospectableM2M \
    DBIx::Class::Journal \
    DBIx::Class::LibXMLdoc \
    DBIx::Class::LookupColumn \
    DBIx::Class::MaterializedPath \
    DBIx::Class::Migration \
    DBIx::Class::Numeric \
    DBIx::Class::Objects \
    DBIx::Class::OptimisticLocking \
    DBIx::Class::ParameterizedJoinHack \
    DBIx::Class::PassphraseColumn \
    DBIx::Class::QueriesTime \
    DBIx::Class::QueryLog \
    DBIx::Class::QueryLog::WithStackTrace \
    DBIx::Class::QueryProfiler \
    DBIx::Class::RandomStringColumns \
    DBIx::Class::Relationship::Predicate \
    DBIx::Class::Report \
    DBIx::Class::Result::ColumnData \
    DBIx::Class::ResultSet::AccessorsEverywhere \
    DBIx::Class::ResultSet::Data::Pageset \
    DBIx::Class::ResultSet::Excel \
    DBIx::Class::ResultSet::Faceter \
    DBIx::Class::ResultSet::HashRef \
    DBIx::Class::ResultSet::RecursiveUpdate \
    DBIx::Class::Result::Validation \
    DBIx::Class::SaltedPasswords \
    DBIx::Class::Schema::Config \
    DBIx::Class::Schema::Diff \
    DBIx::Class::Schema::RestrictWithObject \
    DBIx::Class::Schema::ResultSetAccessors \
    DBIx::Class::Schema::Versioned::Inline \
    DBIx::Class::Service \
    DBIx::Class::SingletonRows \
    DBIx::Class::Storage::DBI::mysql::backup \
    DBIx::Class::Storage::DBI::ODBC::OPENEDGE \
    DBIx::Class::Storage::DBI::OpenEdge \
    DBIx::Class::StorageReadOnly \
    DBIx::Class::Storage::TxnEndHook \
    DBIx::Class::TimeStamp \
    DBIx::Class::Tokenize \
    DBIx::Class::TopoSort \
    DBIx::Class::Tree::CalculateSets \
    DBIx::Class::Tree::Mobius \
    DBIx::Class::UnicornLogger \
    DBIx::Class::UserStamp \
    DBIx::Class::UUIDColumns \
    DBIx::Class::Validation \
    DBIx::Class::Validation::Structure \
    DBIx::Class::WebForm \
    DBIx::Class::Wrapper \
    DBIx::Table::TestDataGenerator \
    Data::Importer \
    Dwimmer \
    ETLp \
    ExtJS::Generator::DBIC \
    Finance::QuoteDB \
    Form::Processor::Model::DBIC \
    Form::Sensible::Reflector::DBIC \
    FormValidator::Simple::Plugin::DBIC::Unique \
    Galileo \
    GenOO \
    HTML::FormFu::ExtJS \
    HTML::FormFu::Model::DBIC \
    HTML::FormHandler::Model::DBIC \
    Hyle \
    IronMan::Schema \
    KiokuDB::Backend::DBI \
    Log::Log4perl::Appender::DBIx::Class \
    Mixin::ExtraFields::Driver::DBIC \
    Module::CPANTS::ProcessCPAN \
    Mojolicious::Plugin::DBICAdmin \
    MooseX::Types::DBIx::Class \
    OpusVL::AppKit \
    OpusVL::AppKit::Schema::AppKitAuthDB \
    OpusVL::Preferences \
    OpusVL::SysParams \
    Prosody \
    Pulp \
    RackMan \
    Reaction \
    Schema::RackTables \
    Tapper::MCP \
    Tapper::Schema \
    Template::Provider::CustomDBIC \
    Template::Provider::DBIC \
    Template::Provider::PerContextDBIC \
    Template::Provider::PrefixDBIC \
    Test::DBIC::ExpectedQueries \
    Test::DBIC::Schema::Connector \
    Test::DBIx::Class::Schema \
    Test::Fixture::DBIC::Schema \
    Tie::DBIx::Class \
    Types::DBIx::Class \
    WebAPI::DBIC \
    WebNano::Controller::CRUD \
    Web::Util::DBIC::Paging \
    Web::Util::ExtPaging \
    WWW::Hashbang::Pastebin \
    WWW::RobotRules::DBIC \
    YAWF \
    YATT::Lite \
    Yeb::Plugin::DBIC \
    "DBD::SQLite@1.35 Catalyst::ActionRole::BuildDBICResult DBIx::NoSQL Jedi::Plugin::Session Jedi::Plugin::Auth" \
    "Test::More@1.001014 Test::DBIx::Class::Stats" \
    "Mojolicious@3.91 ExpenseTracker" \
    "Dancer2@0.166001 Strehler Strehler::Element::Extra Strehler::RSS" \
; do \
  cpanm -v --reinstall $d 2>&1 \
| tee -a /dev/shm/umpfh \
| grep -P -B1 '^(Building and testing|Result:)' || exit 1 \
; done

echo
echo 'YAY!'
exit 0
17 files changed:
Changes
examples/Schema/MyApp/Schema.pm
lib/DBIx/Class/MethodAttributes.pm
lib/DBIx/Class/Schema.pm
lib/DBIx/Class/Schema/SanityChecker.pm [new file with mode: 0644]
lib/DBIx/Class/Schema/Versioned.pm
lib/DBIx/Class/_Util.pm
t/cdbi/DeepAbstractSearch/01_search.t
t/cdbi/testlib/DBIC/Test/SQLite.pm
t/cdbi/testlib/MyBase.pm
t/lib/DBICTest/BaseSchema.pm
t/storage/txn.t
t/storage/txn_scope_guard.t
xt/dist/pod_coverage.t
xt/extra/diagnostics/invalid_component_composition.t [new file with mode: 0644]
xt/extra/internals/ithread_stress.t
xt/extra/lean_startup.t