Git Product home page Git Product logo

pg_ha_migrations's Introduction

PgHaMigrations

Build Status

We've documented our learned best practices for applying schema changes without downtime in the post PostgreSQL at Scale: Database Schema Changes Without Downtime on the PayPal Technology Blog. Many of the approaches we take and choices we've made are explained in much greater depth there than in this README.

Internally we apply those best practices to our Rails applications through this gem which updates ActiveRecord migrations to clearly delineate safe and unsafe DDL as well as provide safe alternatives where possible.

Some projects attempt to hide complexity by having code determine the intent and magically do the right series of operations. But we (and by extension this gem) take the approach that it's better to understand exactly what the database is doing so that (particularly long running) operations are not a surprise during your deploy cycle.

Provided functionality:

Installation

Add this line to your application's Gemfile:

gem 'pg_ha_migrations'

And then execute:

$ bundle

Or install it yourself as:

$ gem install pg_ha_migrations

Usage

Rollback

Because we require that "Rollback strategies do not involve reverting the database schema to its previous version", PgHaMigrations does not support ActiveRecord's automatic migration rollback capability.

Instead we write all of our migrations with only an def up method like:

def up
  safe_add_column :table, :column
end

and never use def change. We believe that this is the only safe approach in production environments. For development environments we iterate by recreating the database from scratch every time we make a change.

Migrations

There are two major classes of concerns we try to handle in the API:

  • Database safety (e.g., long-held locks)
  • Application safety (e.g., dropping columns the app uses)

We rename migration methods with prefixes denoting their safety level:

  • safe_*: These methods check for both application and database safety concerns, prefer concurrent operations where available, set low lock timeouts where appropriate, and decompose operations into multiple safe steps.
  • unsafe_*: These methods are generally a direct dispatch to the native ActiveRecord migration method.

Calling the original migration methods without a prefix will raise an error.

The API is designed to be explicit yet remain flexible. There may be situations where invoking the unsafe_* method is preferred (or the only option available for definitionally unsafe operations).

While unsafe_* methods were historically (through 1.0) pure wrappers for invoking the native ActiveRecord migration method, there is a class of problems that we can't handle easily without breaking that design rule a bit. For example, dropping a column is unsafe from an application perspective, so we make the application safety concerns explicit by using an unsafe_ prefix. Using unsafe_remove_column calls out the need to audit the application to confirm the migration won't break the application. Because there are no safe alternatives we don't define a safe_remove_column analogue. However there are still conditions we'd like to assert before dropping a column. For example, dropping an unused column that's used in one or more indexes may be safe from an application perspective, but the cascading drop of the index won't use a CONCURRENT operation to drop the dependent indexes and is therefore unsafe from a database perspective.

When unsafe_* migration methods support checks of this type you can bypass the checks by passing an :allow_dependent_objects key in the method's options hash containing an array of dependent object types you'd like to allow. Until 2.0 none of these checks will run by default, but you can opt-in by setting config.check_for_dependent_objects = true in your configuration initializer.

Similarly we believe the force: true option to ActiveRecord's create_table method is always unsafe, and therefore we disallow it even when calling unsafe_create_table. This option won't be enabled by default until 2.0, but you can opt-in by setting config.allow_force_create_table = false in your configuration initializer.

Running multiple DDL statements inside a transaction acquires exclusive locks on all of the modified objects. For that reason, this gem disables DDL transactions by default. You can change this by resetting ActiveRecord::Migration.disable_ddl_transaction in your application.

The following functionality is currently unsupported:

  • Rollbacks
  • Generators
  • schema.rb

safe_create_table

Safely creates a new table.

safe_create_table :table do |t|
  t.type :column
end

safe_create_enum_type

Safely create a new enum without values.

safe_create_enum_type :enum

Or, safely create the enum with values.

safe_create_enum_type :enum, ["value1", "value2"]

safe_add_enum_value

Safely add a new enum value.

safe_add_enum_value :enum, "value"

unsafe_rename_enum_value

Unsafely change the value of an enum type entry.

unsafe_rename_enum_value(:enum, "old_value", "new_value")

Note:

Changing an enum value does not issue any long-running scans or acquire locks on usages of the enum type. Therefore multiple queries within a transaction concurrent with the change may see both the old and new values. To highlight these potential pitfalls no safe_rename_enum_value equivalent exists. Before modifying an enum type entry you should verify that no concurrently executing queries will attempt to write the old value and that read queries understand the new value.

safe_add_column

Safely add a column.

safe_add_column :table, :column, :type

unsafe_add_column

Unsafely add a column, but do so with a lock that is safely acquired.

unsafe_add_column :table, :column, :type

safe_change_column_default

Safely change the default value for a column.

# Constant value:
safe_change_column_default :table, :column, "value"
safe_change_column_default :table, :column, DateTime.new(...)
# Functional expression evaluated at row insert time:
safe_change_column_default :table, :column, -> { "NOW()" }
# Functional expression evaluated at migration time:
safe_change_column_default :table, :column, -> { "'NOW()'" }

Note: On Postgres 11+ adding a column with a constant default value does not rewrite or scan the table (under a lock or otherwise). In that case a migration adding a column with a default should do so in a single operation rather than the two-step safe_add_column followed by safe_change_column_default. We enforce this best practice with the error PgHaMigrations::BestPracticeError, but if your prefer otherwise (or are running in a mixed Postgres version environment), you may opt out by setting config.prefer_single_step_column_addition_with_default = true in your configuration initializer.

safe_make_column_nullable

Safely make the column nullable.

safe_make_column_nullable :table, :column

unsafe_make_column_not_nullable

Unsafely make a column not nullable.

unsafe_make_column_not_nullable :table, :column

safe_add_concurrent_index

Add an index concurrently.

safe_add_concurrent_index :table, :column

Add a composite btree index.

safe_add_concurrent_index :table, [:column1, :column2], name: "index_name", using: :btree

safe_remove_concurrent_index

Safely remove an index. Migrations that contain this statement must also include disable_ddl_transaction!.

safe_remove_concurrent_index :table, :name => :index_name

safe_add_unvalidated_check_constraint

Safely add a CHECK constraint. The constraint will not be immediately validated on existing rows to avoid a full table scan while holding an exclusive lock. After adding the constraint, you'll need to use safe_validate_check_constraint to validate existing rows.

safe_add_unvalidated_check_constraint :table, "column LIKE 'example%'", name: :constraint_table_on_column_like_example

safe_validate_check_constraint

Safely validate (without acquiring an exclusive lock) existing rows for a newly added but as-yet unvalidated CHECK constraint.

safe_validate_check_constraint :table, name: :constraint_table_on_column_like_example

safe_rename_constraint

Safely rename any (not just CHECK) constraint.

safe_rename_constraint :table, from: :constraint_table_on_column_like_typo, to: :constraint_table_on_column_like_example

unsafe_remove_constraint

Drop any (not just CHECK) constraint.

unsafe_remove_constraint :table, name: :constraint_table_on_column_like_example

safe_create_partitioned_table

Safely create a new partitioned table using declaritive partitioning.

# list partitioned table using single column as partition key
safe_create_partitioned_table :table, type: :list, key: :example_column do |t|
  t.text :example_column, null: false
end

# range partitioned table using multiple columns as partition key
safe_create_partitioned_table :table, type: :range, key: [:example_column_a, :example_column_b] do |t|
  t.integer :example_column_a, null: false
  t.integer :example_column_b, null: false
end

# hash partitioned table using expression as partition key
safe_create_partitioned_table :table, :type: :hash, key: ->{ "(example_column::date)" } do |t|
  t.datetime :example_column, null: false
end

The identifier column type is bigserial by default. This can be overridden, as you would in safe_create_table, by setting the id argument:

safe_create_partitioned_table :table, id: :serial, type: :range, key: :example_column do |t|
  t.date :example_column, null: false
end

In PostgreSQL 11+, primary key constraints are supported on partitioned tables given the partition key is included. On supported versions, the primary key is inferred by default (see available options). This functionality can be overridden by setting the infer_primary_key argument.

# primary key will be (id, example_column)
safe_create_partitioned_table :table, type: :range, key: :example_column do |t|
  t.date :example_column, null: false
end

# primary key will not be created
safe_create_partitioned_table :table, type: :range, key: :example_column, infer_primary_key: false do |t|
  t.date :example_column, null: false
end

Utilities

safely_acquire_lock_for_table

Safely acquire a lock for a table.

safely_acquire_lock_for_table(:table) do
  ...
end

adjust_lock_timeout

Adjust lock timeout.

adjust_lock_timeout(seconds) do
  ...
end

adjust_statement_timeout

Adjust statement timeout.

adjust_statement_timeout(seconds) do
  ...
end

safe_set_maintenance_work_mem_gb

Set maintenance work mem.

safe_set_maintenance_work_mem_gb 1

Configuration

The gem can be configured in an initializer.

PgHaMigrations.configure do |config|
  # ...
end

Available options

  • disable_default_migration_methods: If true, the default implementations of DDL changes in ActiveRecord::Migration and the PostgreSQL adapter will be overridden by implementations that raise a PgHaMigrations::UnsafeMigrationError. Default: true
  • check_for_dependent_objects: If true, some unsafe_* migration methods will raise a PgHaMigrations::UnsafeMigrationError if any dependent objects exist. Default: false
  • prefer_single_step_column_addition_with_default: If true, raise an error when adding a column and separately setting a constant default value for that column in the same migration. Default: false
  • allow_force_create_table: If false, the force: true option to ActiveRecord's create_table method is disallowed. Default: true
  • infer_primary_key_on_partitioned_tables: If true, the primary key for partitioned tables will be inferred on PostgreSQL 11+ databases (identifier column + partition key columns). Default: true

Rake Tasks

Use this to check for blocking transactions before migrating.

$ bundle exec rake pg_ha_migrations:check_blocking_database_transactions

This rake task expects that you already have a connection open to your database. We suggest that you add another rake task to open the connection and then add that as a prerequisite for pg_ha_migrations:check_blocking_database_transactions.

namespace :db do
  desc "Establish a database connection"
  task :establish_connection do
    ActiveRecord::Base.establish_connection
  end
end

Rake::Task["pg_ha_migrations:check_blocking_database_transactions"].enhance ["db:establish_connection"]

Development

After checking out the repo, run bin/setup to install dependencies and start a postgres docker container. Then, run bundle exec rspec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment. This project uses Appraisal to test against multiple versions of ActiveRecord; you can run the tests against all supported version with bundle exec appraisal rspec.

Running tests will automatically create a test database in the locally running Postgres server. You can find the connection parameters in spec/spec_helper.rb, but setting the environment variables PGHOST, PGPORT, PGUSER, and PGPASSWORD will override the defaults.

To install this gem onto your local machine, run bundle exec rake install.

To release a new version, update the version number in version.rb, commit the change, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Note: if while releasing the gem you get the error Your rubygems.org credentials aren't set. Run `gem push` to set them. you can more simply run gem signin.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/braintreeps/pg_ha_migrations. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the PgHaMigrations project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.

pg_ha_migrations's People

Contributors

alexmooney avatar braintree-security avatar braintreeps avatar brettjnorris avatar celeen avatar coopergillan avatar jaronkk avatar jcoleman avatar jonmr avatar rkrage avatar roman-melnyk avatar schalkwijk avatar sdqali avatar stephenreid avatar tragiclifestories avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pg_ha_migrations's Issues

Safe Lock Acquisition Should Specifically Target Different Lock Modes

Currently safely_acquire_lock_for_table always acquires an AccessExclusiveLock, which is generally safe in the sense that:

  • It's always sufficient for the operation since it's the highest lock type for a table.
  • Most of the places we use it need that high of a lock level anyway.

However, it has a few disadvantages:

  • There are some existing places where we don't need that high of a lock level (albeit the operations are still fast, so it hasn't seemed like a big deal).
  • We could use it in places where we explicitly do not need a high level lock.

If we change this, we'd need t make sure that nesting calls (or even calls in the same transaction -- even though we discourage that) can't upgrade locks since that's could result in a deadlock.

Definition of `unsafe_add_column` is unused

While trying to test some lock scenarios, I spotted this little problem.

Basically, unlike all the other unsafe_ methods, this one - along with unsafe_make_column_not_nullable - is supposed to have a custom implementation that uses the lock acquisition logic rather than delegating directly to ActiveRecord::Migration. However, the custom implementation are further away in the inheritance chain, so it never gets called - the delegate in unsafe_statements gets called instead.

I thought initially that this was a bug I'd introduced, but I've reproduced it on 1.0.0 as well.

Fix should be as simple as removing the delegate from unsafe_statements ...

EDIT: only unsafe_add_column is affected, not unsafe_make_column_not_nullable as I initially thought.

Add Open Source Testing

In order to increase quality and ability to verify pull requests, add Travis open source CI testing

Upgrade to Rails 6

Hey there.

I would love to use this gem for one of our projects.

I just wanted to know: What's the timeline and roadmap for moving to Rails 6?

If there isn't one, is there anything I can do to help? And is there anything I should be particularly observant about when I start to do work?

Best,

Feature request: make add/remove concurrent index work together in a single "change" migration

To properly set up a concurrent index migration that can be rolled back, it looks like this:

class AddUserEmailIndex < ActiveRecord::Migration[5.2]
  disable_ddl_transaction!

  def up
    safe_add_concurrent_index :users, :email, unique: true
  end

  def down
    safe_remove_concurrent_index :users, :email, unique: true
  end
end

IMO, ideally it would look like this

class AddUserEmailIndex < ActiveRecord::Migration[5.2]
  disable_ddl_transaction!

  def change
    safe_add_concurrent_index :users, :email, unique: true
  end
end

and the gem would be smart enough to run safe_remove_concurrent_index on the rollback without needing separate up/down methods.

Setup travis builds

We should have travis builds running. Ideally, we'd test against multiple versions of rails.

Enforce certain safety features even in unsafe_* by default

From discussion in #22:

...it might well be the case that all of the unsafe variants (where it's relevant) should use the safe lock acquisition. I think that's mostly a project policy discussion, since it means the unsafe variants are increasingly not just proxies for the original Rails method (though we've already gone down that path somewhat with the dependent object checks).

ISTM that what we might want here is to move from 2 layers (safe_* and unsafe_*) to 3 layers: safe_*, unsafe_* to prevent clear database level issues (e.g., provide safe locking), and insert a new layer (maybe raw_*?) to be a pure dispatch into the original ActiveRecord implementation.

Allow NOT NULL on new columns using atthasmissing style default value optimization

On top of the work #51 did, we should also allow setting NOT NULL on any new column that can take advantage of PG11+'s ability to add a new column with a default without rewriting the table.

This isn't strictly documented at https://www.postgresql.org/docs/11/ddl-alter.html#DDL-ALTER-ADDING-A-COLUMN, but these code references show it's correct:
https://github.com/postgres/postgres/blob/f3faf35f370f558670c8213a08f2683f3811ffc7/src/backend/catalog/heap.c#L2267
https://github.com/postgres/postgres/blob/f3faf35f370f558670c8213a08f2683f3811ffc7/src/backend/commands/tablecmds.c#L6285-L6293

Doing a rollback on safe_create_table fails

Example migration file:

# db/migrate/20190124002047_create_jwt_blacklist.rb
class CreateJwtBlacklist < ActiveRecord::Migration[5.2]
  def change
    safe_create_table :jwt_blacklist do |t|
      t.string :jti, null: false
    end
  end
end

Error:

== 20190124002047 CreateJwtBlacklist: reverting ===============================
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:

wrong number of arguments (given 2, expected 1)
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/pg_ha_migrations-0.2.0/lib/pg_ha_migrations/unsafe_statements.rb:28:in `drop_table'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:687:in `block in revert'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:686:in `each'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:686:in `revert'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:812:in `exec_migration'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/pg_ha_migrations-0.2.0/lib/pg_ha_migrations/safe_statements.rb:102:in `exec_migration'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:798:in `block (2 levels) in migrate'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:797:in `block in migrate'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/connection_adapters/abstract/connection_pool.rb:416:in `with_connection'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:796:in `migrate'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:977:in `migrate'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:1292:in `block in execute_migration_in_transaction'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:1343:in `block in ddl_transaction'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/connection_adapters/abstract/database_statements.rb:259:in `block in transaction'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/connection_adapters/abstract/transaction.rb:239:in `block in within_new_transaction'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/connection_adapters/abstract/transaction.rb:236:in `within_new_transaction'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/connection_adapters/abstract/database_statements.rb:259:in `transaction'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/transactions.rb:212:in `transaction'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:1343:in `ddl_transaction'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:1291:in `execute_migration_in_transaction'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:1263:in `block in migrate_without_lock'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:1262:in `each'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:1262:in `migrate_without_lock'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:1210:in `block in migrate'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:1363:in `with_advisory_lock'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:1210:in `migrate'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:1046:in `down'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:1156:in `move'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/migration.rb:1022:in `rollback'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/gems/activerecord-5.2.2/lib/active_record/railties/databases.rake:142:in `block (2 levels) in <main>'
/Users/dillonwelch/.rvm/rubies/ruby-2.6.1/bin/bundle:23:in `load'
/Users/dillonwelch/.rvm/rubies/ruby-2.6.1/bin/bundle:23:in `<main>'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/bin/ruby_executable_hooks:24:in `eval'
/Users/dillonwelch/.rvm/gems/ruby-2.6.1/bin/ruby_executable_hooks:24:in `<main>'

Guide user from two-step add column / set default to single-step add column with default in PG11

We've historically considered adding a column with a default to be unsafe since that triggered a full table rewrite while holding an exclusive lock.

However given that on PG11+ (and still on 11+ if the default is volatile) we can adding a column with a default without triggering that scan we should encourage users to just do:

safe_add_column :widgets, :name, :default => "blob"[, :null => false]

rather than

safe_add_column :widgets, :name
safe_change_column_default :widgets, :name

I'm envisioning adding a tracking mechanism to safe_add_column (storing a list of columns added per-migration in memory) so that safe_change_column_default could raise an error if that column was just created in the same migration.

Feature request: Methods for adding generic constraints

Maybe a triplet something like:

  • safe_add_constraint_not_valid
  • safe_validate_constraint
  • unsafe_add_constraint

We need to ensure these validate the length of the constraint names.

Do we want separate methods for various types? Should this be CHECK constraint specific?

Unsafe make_column_not_nullable

In the method unsafe_make_column_not_nullable, it's called safely_acquire_lock_for_table.

Is the prefix unsafe_ correct?

def unsafe_make_column_not_nullable(table, column, options={}) # options arg is only present for backwards compatiblity
safely_acquire_lock_for_table(table) do
unsafe_execute "ALTER TABLE #{table} ALTER COLUMN #{column} SET NOT NULL"
end
end

Make monkey-patching of ActiveRecord::Migration methods optional

Really glad this exists - thanks people!

One thing that would make things easier is if it was possible not to replace the original implementation of the various ActiveRecord::Migration DDL methods with versions that raise errors. The use case for this is incrementally rolling this out in an organisation with a big old monolith with multiple teams etc. rolling it out. We'd like to have the choice ideally.

Happy to do a PR if one would be accepted.

Upgrade Travis to PG 12

With PostgreSQL 12 being released this summer and 9.6 phasing out on RDS, it may be useful to run the test suite against newer, or at least more versions of Postgres

Replication Backends Now Included in Long-Running Transactions Check

I recently updated the code that reports long-running-transactions to make sure it includes idle transactions. A side-effect is that logical and physical replication walsender backends (`select * from pg_stat_activity where backend_type = 'walsender') are being reported. They need to be excluded since that "transaction" won't block DDL.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.