Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

139 lines
5.7KB

  1. defmodule Pleroma.Repo.Migrations.UsersAndActivitiesFlakeId do
  2. use Ecto.Migration
  3. alias Pleroma.Clippy
  4. require Integer
  5. import Ecto.Query
  6. alias Pleroma.Repo
  7. # This migrates from int serial IDs to custom Flake:
  8. # 1- create a temporary uuid column
  9. # 2- fill this column with compatibility ids (see below)
  10. # 3- remove pkeys constraints
  11. # 4- update relation pkeys with the new ids
  12. # 5- rename the temporary column to id
  13. # 6- re-create the constraints
  14. def up do
  15. # Old serial int ids are transformed to 128bits with extra padding.
  16. # The application (in `Pleroma.FlakeId`) handles theses IDs properly as integers; to keep compatibility
  17. # with previously issued ids.
  18. # execute "update activities set external_id = CAST( LPAD( TO_HEX(id), 32, '0' ) AS uuid);"
  19. # execute "update users set external_id = CAST( LPAD( TO_HEX(id), 32, '0' ) AS uuid);"
  20. clippy = start_clippy_heartbeats()
  21. # Lock both tables to avoid a running server to meddling with our transaction
  22. execute("LOCK TABLE activities;")
  23. execute("LOCK TABLE users;")
  24. execute("""
  25. ALTER TABLE activities
  26. DROP CONSTRAINT activities_pkey CASCADE,
  27. ALTER COLUMN id DROP default,
  28. ALTER COLUMN id SET DATA TYPE uuid USING CAST( LPAD( TO_HEX(id), 32, '0' ) AS uuid),
  29. ADD PRIMARY KEY (id);
  30. """)
  31. execute("""
  32. ALTER TABLE users
  33. DROP CONSTRAINT users_pkey CASCADE,
  34. ALTER COLUMN id DROP default,
  35. ALTER COLUMN id SET DATA TYPE uuid USING CAST( LPAD( TO_HEX(id), 32, '0' ) AS uuid),
  36. ADD PRIMARY KEY (id);
  37. """)
  38. execute(
  39. "UPDATE users SET info = jsonb_set(info, '{pinned_activities}', array_to_json(ARRAY(select jsonb_array_elements_text(info->'pinned_activities')))::jsonb);"
  40. )
  41. # Fkeys:
  42. # Activities - Referenced by:
  43. # TABLE "notifications" CONSTRAINT "notifications_activity_id_fkey" FOREIGN KEY (activity_id) REFERENCES activities(id) ON DELETE CASCADE
  44. # Users - Referenced by:
  45. # TABLE "filters" CONSTRAINT "filters_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
  46. # TABLE "lists" CONSTRAINT "lists_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
  47. # TABLE "notifications" CONSTRAINT "notifications_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
  48. # TABLE "oauth_authorizations" CONSTRAINT "oauth_authorizations_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)
  49. # TABLE "oauth_tokens" CONSTRAINT "oauth_tokens_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)
  50. # TABLE "password_reset_tokens" CONSTRAINT "password_reset_tokens_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)
  51. # TABLE "push_subscriptions" CONSTRAINT "push_subscriptions_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
  52. # TABLE "websub_client_subscriptions" CONSTRAINT "websub_client_subscriptions_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)
  53. execute("""
  54. ALTER TABLE notifications
  55. ALTER COLUMN activity_id SET DATA TYPE uuid USING CAST( LPAD( TO_HEX(activity_id), 32, '0' ) AS uuid),
  56. ADD CONSTRAINT notifications_activity_id_fkey FOREIGN KEY (activity_id) REFERENCES activities(id) ON DELETE CASCADE;
  57. """)
  58. for table <-
  59. ~w(notifications filters lists oauth_authorizations oauth_tokens password_reset_tokens push_subscriptions websub_client_subscriptions) do
  60. execute("""
  61. ALTER TABLE #{table}
  62. ALTER COLUMN user_id SET DATA TYPE uuid USING CAST( LPAD( TO_HEX(user_id), 32, '0' ) AS uuid),
  63. ADD CONSTRAINT #{table}_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
  64. """)
  65. end
  66. flush()
  67. stop_clippy_heartbeats(clippy)
  68. end
  69. def down, do: :ok
  70. defp start_clippy_heartbeats() do
  71. count = from(a in "activities", select: count(a.id)) |> Repo.one!()
  72. if count > 5000 do
  73. heartbeat_interval = :timer.minutes(2) + :timer.seconds(30)
  74. all_tips =
  75. Clippy.tips() ++
  76. [
  77. "The migration is still running, maybe it's time for another “tea”?",
  78. "Happy rabbits practice a cute behavior known as a\n“binky:” they jump up in the air\nand twist\nand spin around!",
  79. "Nothing and everything.\n\nI still work.",
  80. "Pleroma runs on a Raspberry Pi!\n\n … but this migration will take forever if you\nactually run on a raspberry pi",
  81. "Status? Stati? Post? Note? Toot?\nRepeat? Reboost? Boost? Retweet? Retoot??\n\nI-I'm confused."
  82. ]
  83. heartbeat = fn heartbeat, runs, all_tips, tips ->
  84. tips =
  85. if Integer.is_even(runs) do
  86. tips = if tips == [], do: all_tips, else: tips
  87. [tip | tips] = Enum.shuffle(tips)
  88. Clippy.puts(tip)
  89. tips
  90. else
  91. IO.puts(
  92. "\n -- #{DateTime.to_string(DateTime.utc_now())} Migration still running, please wait…\n"
  93. )
  94. tips
  95. end
  96. :timer.sleep(heartbeat_interval)
  97. heartbeat.(heartbeat, runs + 1, all_tips, tips)
  98. end
  99. Clippy.puts([
  100. [:red, :bright, "It looks like you are running an older instance!"],
  101. [""],
  102. [:bright, "This migration may take a long time", :reset, " -- so you probably should"],
  103. ["go drink a cofe, or a tea, or a beer, a whiskey, a vodka,"],
  104. ["while it runs to deal with your temporary fediverse pause!"]
  105. ])
  106. :timer.sleep(heartbeat_interval)
  107. spawn_link(fn -> heartbeat.(heartbeat, 1, all_tips, []) end)
  108. end
  109. end
  110. defp stop_clippy_heartbeats(pid) do
  111. if pid do
  112. Process.unlink(pid)
  113. Process.exit(pid, :kill)
  114. Clippy.puts([[:green, :bright, "Hurray!!", "", "", "Migration completed!"]])
  115. end
  116. end
  117. end