mirror of
https://github.com/zulip/zulip.git
synced 2025-11-07 07:23:22 +00:00
This needs to be done in three South migrations to not block users
from sending messages for a long time. Adding the column requires a
write lock on the zephyr_message table and populating the new column
takes a long time. Thus, we can't do them both in the same
transaction (which South forces on migrations). Additionally,
creating the index takes a lot of computation and needs to lock the
table when not done CONCURRENTLY, which can't be done inside of a
transaction.
To do this manual change, you need to run:
python manage.py migrate zephyr 0007
ssh postgres.humbughq.com 'echo "CREATE INDEX CONCURRENTLY zephyr_message_search_tsvector ON zephyr_message USING gin(search_tsvector);" | psql'
python manage.py migrate zephyr 0008
on staging. No action is required on prod since the database is
shared.
Note that this migration must be done completely before we switch to
using the tsvector cache column.
(imported from commit b6a27013a60c1fd196eabb095d2d11d20bba5aac)
182 lines
12 KiB
Python
182 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
import datetime
|
|
from south.db import db
|
|
from south.v2 import SchemaMigration
|
|
from django.db import models
|
|
from django.conf import settings
|
|
|
|
class Migration(SchemaMigration):
|
|
|
|
def forwards(self, orm):
|
|
# This schema migration is only for use in automated migrations. To
|
|
# deploy on the production database (the migration only needs to be
|
|
# done once for both of staging and prod because they share a
|
|
# database), you should instead execute the following SQL manually:
|
|
#
|
|
# $ ssh postgres.humbughq.com
|
|
# $ psql
|
|
# humbug=> CREATE INDEX CONCURRENTLY zephyr_message_search_tsvector ON zephyr_message USING gin(search_tsvector);
|
|
#
|
|
# Note the addition of the "CONCURRENTLY" keyword. The problem is that
|
|
# creating the index takes non-trivial time and requires a write lock
|
|
# on the table while the index is being created. This would mean that
|
|
# users would be unable to send messages while we were generating the
|
|
# index, which isn't acceptable. We can't create the index
|
|
# concurrently in the South migration because concurrent index
|
|
# creations can't happen inside of a transaction and South forces a
|
|
# transaction on migration functions.
|
|
if "postgres" not in settings.DATABASES["default"]["ENGINE"]:
|
|
return
|
|
if len(db.execute("""SELECT relname FROM pg_class
|
|
WHERE relname = 'zephyr_message_search_tsvector'""")) != 0:
|
|
print "Not creating index because it already exists"
|
|
else:
|
|
db.execute("""CREATE INDEX zephyr_message_search_tsvector ON zephyr_message
|
|
USING gin(search_tsvector)""")
|
|
|
|
def backwards(self, orm):
|
|
if "postgres" not in settings.DATABASES["default"]["ENGINE"]:
|
|
return
|
|
db.execute("DROP INDEX zephyr_message_search_tsvector")
|
|
|
|
models = {
|
|
'auth.group': {
|
|
'Meta': {'object_name': 'Group'},
|
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
|
},
|
|
'auth.permission': {
|
|
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
|
},
|
|
'auth.user': {
|
|
'Meta': {'object_name': 'User'},
|
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
|
},
|
|
'contenttypes.contenttype': {
|
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
|
},
|
|
'zephyr.client': {
|
|
'Meta': {'object_name': 'Client'},
|
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30', 'db_index': 'True'})
|
|
},
|
|
'zephyr.defaultstream': {
|
|
'Meta': {'unique_together': "(('realm', 'stream'),)", 'object_name': 'DefaultStream'},
|
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
|
'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Realm']"}),
|
|
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Stream']"})
|
|
},
|
|
'zephyr.huddle': {
|
|
'Meta': {'object_name': 'Huddle'},
|
|
'huddle_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
|
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
|
},
|
|
'zephyr.message': {
|
|
'Meta': {'object_name': 'Message'},
|
|
'content': ('django.db.models.fields.TextField', [], {}),
|
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
|
'pub_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
|
|
'recipient': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Recipient']"}),
|
|
'sender': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.UserProfile']"}),
|
|
'sending_client': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Client']"}),
|
|
'subject': ('django.db.models.fields.CharField', [], {'max_length': '60', 'db_index': 'True'})
|
|
},
|
|
'zephyr.mituser': {
|
|
'Meta': {'object_name': 'MitUser'},
|
|
'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}),
|
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
|
'status': ('django.db.models.fields.IntegerField', [], {'default': '0'})
|
|
},
|
|
'zephyr.preregistrationuser': {
|
|
'Meta': {'object_name': 'PreregistrationUser'},
|
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
|
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
|
'invited_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
|
'referred_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.UserProfile']", 'null': 'True'}),
|
|
'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
|
'streams': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['zephyr.Stream']", 'null': 'True', 'symmetrical': 'False'})
|
|
},
|
|
'zephyr.realm': {
|
|
'Meta': {'object_name': 'Realm'},
|
|
'domain': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
|
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
|
},
|
|
'zephyr.recipient': {
|
|
'Meta': {'unique_together': "(('type', 'type_id'),)", 'object_name': 'Recipient'},
|
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
|
'type': ('django.db.models.fields.PositiveSmallIntegerField', [], {'db_index': 'True'}),
|
|
'type_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'})
|
|
},
|
|
'zephyr.stream': {
|
|
'Meta': {'unique_together': "(('name', 'realm'),)", 'object_name': 'Stream'},
|
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
|
'invite_only': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
|
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'db_index': 'True'}),
|
|
'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Realm']"})
|
|
},
|
|
'zephyr.streamcolor': {
|
|
'Meta': {'object_name': 'StreamColor'},
|
|
'color': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
|
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
|
'subscription': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Subscription']"})
|
|
},
|
|
'zephyr.subscription': {
|
|
'Meta': {'unique_together': "(('user_profile', 'recipient'),)", 'object_name': 'Subscription'},
|
|
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
|
'recipient': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Recipient']"}),
|
|
'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.UserProfile']"})
|
|
},
|
|
'zephyr.useractivity': {
|
|
'Meta': {'unique_together': "(('user_profile', 'client', 'query'),)", 'object_name': 'UserActivity'},
|
|
'client': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Client']"}),
|
|
'count': ('django.db.models.fields.IntegerField', [], {}),
|
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
|
'last_visit': ('django.db.models.fields.DateTimeField', [], {}),
|
|
'query': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
|
|
'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.UserProfile']"})
|
|
},
|
|
'zephyr.usermessage': {
|
|
'Meta': {'unique_together': "(('user_profile', 'message'),)", 'object_name': 'UserMessage'},
|
|
'archived': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
|
'message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Message']"}),
|
|
'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.UserProfile']"})
|
|
},
|
|
'zephyr.userprofile': {
|
|
'Meta': {'object_name': 'UserProfile'},
|
|
'api_key': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
|
|
'enable_desktop_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
|
'full_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
|
'last_pointer_updater': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
|
|
'pointer': ('django.db.models.fields.IntegerField', [], {}),
|
|
'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Realm']"}),
|
|
'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
|
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
|
|
}
|
|
}
|
|
|
|
complete_apps = ['zephyr']
|