Under Consideration: Migrate the Forums to Discourse

Dear All,

After being active on the Node-RED forum for the last few weeks, I have been very impressed with Discourse, and my eyes have been opened.

https://www.discourse.org/

but not the paid /hosted offering, but using the open distribution:

https://github.com/discourse/discourse

Soon, I will build everything (in a test bed) and attempt to port our core database over to discourse (for testing only).

I am also considering paying around $1000 to $1500 to have a professional who has done this before do the migration; but I need to try it myself first to see if it is better for me to do it myself, or pay to have it done.

I think, seriously, it is time to 'retire' the legacy vBulletin code base and "move on"....

Anyone here ever use discourse forums?

Cheers.

I've never heard
Here it is in my opinion
Keyboard Maestro Discourse - Discussion forum for Keyboard Maestro, the powerful macro program for macOS
Demo on the official website far from our topic

Hi,

In the past i participated sometimes at Monitoring Portal . They changed to discourse 1-2 years ago. The usability seems fine for me. Since discourse is based on ruby on rails(RoR), the resource requirements are higher than the php based vbulletin. I had never run discourse on my own so far. I just had a quick look when searching for a forum solution, and discarded it, because it would have required a newer stronger machine. (Statement of a former programmer-colleague on ruby on rails: "i do not care about memory - developer time is far more costly.").

I had a glance at nodebb, which was very quick in terms of response times. I liked that very much. Home | XCP-ng forum is based on NodeBB. NodeBB is based on Node.js.

phpBB has gotten another major rewrite with version 3.3. I used it as an internal communication board(version 3.2). The administration felt very awful back then. then Since it's fully OSS maybe they constantly improve over time?

All three packages are fully open source.

regards

I like discourse, that is why I decided to go with it (testing, migration testing, etc). No verdict on production, since porting the DB over is fraught with dragons and goblins....

In addition, had a chat with the mods, and both Akshay and Ravinder are on board.

Ravinder loves the features as well how discourse runs on mobile (very important).

Akshay likes as well.

Currently, we are working on building test bed using Docker on a VPS.

Regarding performance, I have been on the Node-RED forum a lot lately (maybe good much), which is discourse, and is it blazing fast.

I think one of the main reasons I like the Node-RED forum, besides a robust Node-RED community, is the fact that discourse is so good and fun to use.

There has not been another forum which has brought be to this "retire old vBulletin" except discourse.

Of course, we can customize discourse as well.... which we will do for sure! That is a given... custom code.

1 Like

Currently chugging along, importing users sans avatars and passwords.

root@localhost-app:/var/www/discourse# su discourse -c 'bundle exec ruby script/import_scripts/vbulletin.rb'
discourse:@localhost wants vb5
Loading existing groups...
Loading existing users...
Loading existing categories...
Loading existing posts...
Loading existing topics...

importing groups...
       20 / 20 (100.0%)  [1918542 items/min]  
importing users
   106664 / 138120 ( 77.2%)  [9832 items/min]      

Just upgraded the test bed slice from 2GB to 8GB and increase RAM and quadrupled the CPU power and disk space (was getting tight)..... Hopefully it will chug along every faster.

8 GB	4 Cores	160 GB SSD	5 TB	40 Gbps	5000 Mbps

This server is only for migration and test purposes.

Let's see how it progresses......

IF (and that is a BIG IF at this time) and the migration goes well, will consider the next steps, for example, where to host the docker image, etc. But I don't want to get ahead of the tips of my skis since we have no idea how the migration will go.

I may also consider porting over the legacy user avatars.... not sure about this yet.

3 Likes

Users imported... then another error:

importing users
   138120 / 138120 (100.0%)  [953 items/min]       
Creating groups membership...
	Unregistered_Not_Log
	Registered
	Users_Awaiting_Email
	_COPPA_Users_Awaitin
	Super_Moderators
	Administrators
	Read_Only
	Moderators1
	Banned_by_Moderators
	Advisors
	Supporter
	Bouncing_Email
	Elite_Member
	VIP_Member
	Signatures
	Recruiter
	Robots
	Novice_Users
	Event_Predictions_Ma
	Banned_User_IDs

importing top level categories...
        8 / 8 (100.0%)  [554 items/min]  in]  
importing children categories...
Traceback (most recent call last):
	5: from script/import_scripts/vbulletin.rb:942:in `<main>'
	4: from /var/www/discourse/script/import_scripts/base.rb:47:in `perform'
	3: from script/import_scripts/vbulletin.rb:84:in `execute'
	2: from script/import_scripts/vbulletin.rb:287:in `import_categories'
	1: from script/import_scripts/vbulletin.rb:287:in `each'
script/import_scripts/vbulletin.rb:289:in `block in import_categories': undefined method `[]' for nil:NilClass (NoMethodError)

Try again:

root@localhost-app:/var/www/discourse# su discourse -c 'bundle exec ruby script/import_scripts/vbulletin.rb'
1 Like

Work around for:

importing children categories...
Traceback (most recent call last):
	5: from script/import_scripts/vbulletin.rb:942:in `<main>'
	4: from /var/www/discourse/script/import_scripts/base.rb:47:in `perform'
	3: from script/import_scripts/vbulletin.rb:84:in `execute'
	2: from script/import_scripts/vbulletin.rb:287:in `import_categories'
	1: from script/import_scripts/vbulletin.rb:287:in `each'
script/import_scripts/vbulletin.rb:289:in `block in import_categories': undefined method `[]' for nil:NilClass (NoMethodError)

Workaround (temporary?); Commented out this "buggy section" with :

if false

....
....

end

Now moving forward again:

importing children categories...

neo skipping...

importing topics...
      810 / 240477 (  0.3%)  [413 items/min]  

Estimated Time to Completion (importing topics = threads only): 10 hours

OK... after seeing the results of about 40% of the main topics / threads (before the replies / posts are uploaded):

importing topics...
    95440 / 240477 ( 39.7%)  [414 items/min

I can see two problems:

  • The CODE and ICODE tags seems to not be ported into discourse.
  • The "not parent" categories of forums (child forums) are not getting moved into the new DB and case an "error".

So, current way ahead is looking like:

  • Continue to let the migration process run until finished.
  • Study the results and at least do the following:
  1. Move avatars to the DUMPED DB before DUMPING, so the AVATARS will port (maybe).
  2. DUMP the Forum DB again.
  3. Do a search and replace in the DB (DUMPED VERSION) and change all CODE tags to ``` (markdown three back ticks).
  4. Ditto for the ICODE tags, convert the all to ``` for markdown ; but I am not sure 100% this will work, but we can try.
  5. Change all forums to "parent, top level forums" in the DUMPED DB, so we I import again, they will work (not be children to parents).

Something like that.....

Looks like the CODE tags are just getting stripped out of the migration. Need to confirm when we get back up to 2019 / 2020 time frame. Right now the migrations is in the 2009 time frame.

This is what is looks like so far..... take lessons learned from this migration, make adjustments, and do it again.

2 Likes

Nice to watch and read the progress.

10 hours is a hell of a roundtrip time. Maybe 1000 or 10000 posts are enough per try ;-).

Update:

Looks like the CODE tags are migrating OK, better than I thought looking at the earlier results... that is a big (and very good) surprise. I am however seeing other BBCODE tags not working (some custom and some basic) but at least they are being transferred and not stripped out, so we can deal with this easy enough later. This is very encouraging.

During topics / threads migration, there were a few errors

   201290 / 240477 ( 83.7%)  [412 items/min]  Exception while creating post thread-243715. Skipping......
   201308 / 240477 ( 83.7%)  [411 items/min]  Exception while creating post thread-243734. Skipping. .............
   201335 / 240477 ( 83.7%)  [411 items/min]  Exception while creating post thread-243762. Skipping............

and so that part of the topic migration ended up:

   239384 / 240477 ( 99.5%)  [400 items/min]  

Not bad for a first run.

Now, the status is:

importing posts...
   112634 / 651396 ( 17.3%)  [607 items/min]  

Estimated time to completion of posts (only posts part): 15 Hours

Example of one of the three "topic errors":

   201335 / 240477 ( 83.7%)  [411 items/min]  Exception while creating post thread-243762. Skipping.
PG::InFailedSqlTransaction: ERROR:  current transaction is aborted, commands ignored until end of transaction block
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-mini-profiler-1.1.6/lib/patches/db/pg.rb:69:in `exec_params'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-mini-profiler-1.1.6/lib/patches/db/pg.rb:69:in `exec_params'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/postgresql_adapter.rb:672:in `block (2 levels) in exec_no_cache'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/dependencies/interlock.rb:48:in `block in permit_concurrent_loads'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/concurrency/share_lock.rb:187:in `yield_shares'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/dependencies/interlock.rb:47:in `permit_concurrent_loads'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/postgresql_adapter.rb:671:in `block in exec_no_cache'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:718:in `block (2 levels) in log'
/usr/local/lib/ruby/2.6.0/monitor.rb:235:in `mon_synchronize'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:717:in `block in log'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/notifications/instrumenter.rb:24:in `instrument'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:708:in `log'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/postgresql_adapter.rb:670:in `exec_no_cache'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/postgresql_adapter.rb:651:in `execute_and_clear'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/postgresql/database_statements.rb:111:in `exec_delete'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/abstract/database_statements.rb:180:in `delete'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/abstract/query_cache.rb:22:in `delete'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/relation.rb:576:in `delete_all'
/var/www/discourse/app/models/topic_link.rb:285:in `cleanup_entries'
/var/www/discourse/app/models/topic_link.rb:139:in `extract_from'
/var/www/discourse/lib/post_creator.rb:572:in `extract_links'
/var/www/discourse/lib/post_creator.rb:180:in `block in create'
/var/www/discourse/lib/post_creator.rb:359:in `block in transaction'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/abstract/database_statements.rb:281:in `block in transaction'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/abstract/transaction.rb:280:in `block in within_new_transaction'
/usr/local/lib/ruby/2.6.0/monitor.rb:235:in `mon_synchronize'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/abstract/transaction.rb:278:in `within_new_transaction'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/abstract/database_statements.rb:281:in `transaction'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/transactions.rb:212:in `transaction'
/var/www/discourse/lib/post_creator.rb:358:in `transaction'
/var/www/discourse/lib/post_creator.rb:174:in `create'
/var/www/discourse/script/import_scripts/base.rb:567:in `create_post'
/var/www/discourse/script/import_scripts/base.rb:515:in `block in create_posts'
/var/www/discourse/script/import_scripts/base.rb:502:in `each'
/var/www/discourse/script/import_scripts/base.rb:502:in `create_posts'
script/import_scripts/vbulletin.rb:330:in `block in import_topics'
/var/www/discourse/script/import_scripts/base.rb:882:in `block in batches'
/var/www/discourse/script/import_scripts/base.rb:881:in `loop'
/var/www/discourse/script/import_scripts/base.rb:881:in `batches'
script/import_scripts/vbulletin.rb:313:in `import_topics'
script/import_scripts/vbulletin.rb:85:in `execute'
/var/www/discourse/script/import_scripts/base.rb:47:in `perform'
script/import_scripts/vbulletin.rb:944:in `<main>'

For those interested; here is a markdown cheatsheet:

Markdown Cheatsheet . adam-p/markdown-here Wiki . GitHub

I used this cheat sheet to add some simple code to transform ICODE , MOD and NOPARSE tags, like this:

But will need to run the migration again from scratch (or learn how to rake or otherwise modify the DB in discourse) after I figure out all the custom Ruby code I need to add to the migration script.

However, I'm learning Ruby a little bit now, something I never thought I would every do.

2 Likes

To me perfectly honest, this is actually pretty easy to do (technically speaking), and I think I can easily improve and customize this migration script with a few iterations.

Note:

Some discourse plugins like this one might work for COLOR:

GitHub - discourse/discourse-bbcode-color: A Discourse Plugin to support BBCode color tags.

And perhaps this some for some subset of other BBCODE tags:

GitHub - discourse/discourse-bbcode: vBulletin BBCode plugin

1 Like

A few more small errors cropped up:

   152569 / 651396 ( 23.4%)  [581 items/min]     201335 / 240477 ( 83.7%)  [411 items/min]  Exception while creating post thread-243762. Skipping.
PG::InFailedSqlTransaction: ERROR:  current transaction is aborted, commands ignored until end of transaction block
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-mini-profiler-1.1.6/lib/patches/db/pg.rb:69:in `exec_params'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-mini-profiler-1.1.6/lib/patches/db/pg.rb:69:in `exec_params'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/postgresql_adapter.rb:672:in `block (2 levels) in exec_no_cache'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/dependencies/interlock.rb:48:in `block in permit_concurrent_loads'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/concurrency/share_lock.rb:187:in `yield_shares'
   152570 / 651396 ( 23.4%)  [581 items/min]  :47:in `permit_concurrent_loads'e_support/dependencies/interlock.rb
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/postgresql_adapter.rb:671:in `block in exec_no_cache'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:718:in `block (2 levels) in log'
/usr/local/lib/ruby/2.6.0/monitor.rb:235:in `mon_synchronize'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:717:in `block in log'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/notifications/instrumenter.rb:24:in `instrument'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:708:in `log'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/postgresql_adapter.rb:670:in `exec_no_cache'
   152571 / 651396 ( 23.4%)  [581 items/min]  ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/postgresql_adapter.rb:651:in `execute_and_clear'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/postgresql/database_statements.rb:111:in `exec_delete'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/abstract/database_statements.rb:180:in `delete'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/abstract/query_cache.rb:22:in `delete'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/relation.rb:576:in `delete_all'
/var/www/discourse/app/models/topic_link.rb:285:in `cleanup_entries'
/var/www/discourse/app/models/topic_link.rb:139:in `extract_from'
/var/www/discourse/lib/post_creator.rb:572:in `extract_links'
/var/www/discourse/lib/post_creator.rb:180:in `block in create'
   152572 / 651396 ( 23.4%)  [581 items/min]  action'in trans
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/abstract/database_statements.rb:281:in `block in transaction'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/abstract/transaction.rb:280:in `block in within_new_transaction'
/usr/local/lib/ruby/2.6.0/monitor.rb:235:in `mon_synchronize'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/abstract/transaction.rb:278:in `within_new_transaction'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/connection_adapters/abstract/database_statements.rb:281:in `transaction'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/transactions.rb:212:in `transaction'
/var/www/discourse/lib/post_creator.rb:358:in `transaction'
/var/www/discourse/lib/post_creator.rb:174:in `create'

But overall, still going OK:

 200362 / 651396 ( 30.8%)  [586 items/min] 

Status Update:

   262021 / 651396 ( 40.2%)  [590 items/min] 

Reported bug in the vbulletin.rb script at meta discourse but will not post any more bugs regarding vB3.8.X migration to discourse over there.

Back to working on the migration.

Also, I am also considering to keep both forums up and running and leave this one at www.unix.com and keep the discourse.unix.com forum separate. Need to think about this and how it might hurt SEO for one site or the other in the future..

Note:

I compared the "forums which ported to categories" against "forums which did not port to discourse categories" and the forums "which did not port" all had a parentid of -1 ,

So, I think this can be fixed by altering the "dumped DB" and changing all forum parentid to -1 .

I will do this as a part of "round 2" after this migration test is done.

forum Table:

Sidebar:

OK... looking at this:

discourse/vbulletin.rb at master . discourse/discourse . GitHub

The bug is in this block:

 def import_categories
    puts "", "importing top level categories..."

    categories = mysql_query("SELECT forumid, title, description, displayorder, parentid FROM #{TABLE_PREFIX}forum ORDER BY forumid").to_a

    top_level_categories = categories.select { |c| c["parentid"] > -1 }

    create_categories(top_level_categories) do |category|
      {
        id: category["forumid"],
        name: @htmlentities.decode(category["title"]).strip,
        position: category["displayorder"],
        description: @htmlentities.decode(category["description"]).strip
      }
    end

    puts "", "importing children categories..."

    children_categories = categories.select { |c| c["parentid"] != -1 }
    top_level_category_ids = Set.new(top_level_categories.map { |c| c["forumid"] })

    # cut down the tree to only 2 levels of categories
    children_categories.each do |cc|
      while !top_level_category_ids.include?(cc["parentid"])
        cc["parentid"] = categories.detect { |c| c["forumid"] == cc["parentid"] }["parentid"]
      end
    end

    create_categories(children_categories) do |category|
      {
        id: category["forumid"],
        name: @htmlentities.decode(category["title"]).strip,
        position: category["displayorder"],
        description: @htmlentities.decode(category["description"]).strip,
        parent_category_id: category_id_from_imported_category_id(category["parentid"])
      }
    end
  end

On work around I have which am looking at is to just rerun this script with a small change, making all categories top level for now:

 top_level_categories = categories.select { |c| c["parentid"]  > -2}.  # make all forums top-level categories

    create_categories(top_level_categories) do |category|
      {
        id: category["forumid"],
        name: @htmlentities.decode(category["title"]).strip,
        position: category["displayorder"],
        description: @htmlentities.decode(category["description"]).strip
      }
    end

and "false out"

if false
children_categories = categories.select { |c| c["parentid"] != -1 }
    top_level_category_ids = Set.new(top_level_categories.map { |c| c["forumid"] })

    # cut down the tree to only 2 levels of categories
    children_categories.each do |cc|
      while !top_level_category_ids.include?(cc["parentid"])
        cc["parentid"] = categories.detect { |c| c["forumid"] == cc["parentid"] }["parentid"]
      end
    end

    create_categories(children_categories) do |category|
      {
        id: category["forumid"],
        name: @htmlentities.decode(category["title"]).strip,
        position: category["displayorder"],
        description: @htmlentities.decode(category["description"]).strip,
        parent_category_id: category_id_from_imported_category_id(category["parentid"])
      }
    end
end

This might work better.

I'm fairly confident now that, after the first "test migration" if over (in a day or so) , I can make this port work nicely with some simple modifications to the Ruby import script in the area of:

Forums to Categories
Custom BB Code Tags

Now, waiting for all the posts / replies to import and then the attachments.

I think there is a potential problem with attachment, so after it is all "done" in this test round, I will have info info to make more adjustments in the Ruby migration script.

Plus, I get to learn a bit of "Ruby" along the way. :slight_smile:

Still chugging along:

   380575 / 651396 ( 58.4%)  [585 items/min]

FYI, discourse plugins on GitHub:

Search . topic:discourse-plugin org:discourse fork:true . GitHub

Still moving along:

   463672 / 651396 ( 71.2%)  [579 items/min]

The only "cautionary" comment I have so far is that of the two people on the "discourse side of the house" I have interacted with so far, both "had their hands out" looking to make money off this migration. One guy was a hired gun "migration expert" who wanted money to help me and the other person was in the discourse forums, the other was a discourse server provider.

This jives with some of the posts I have read which stated that discourse was free and the developers and other make a lot of money on associated hosting and services.

That's a lot different than the Node-RED community I have been interacting with where both Node-RED is free and the community is a robust, friendly group of "makers" all showing off their skills and talents and sometimes over-projecting their skills to attract attention; but they are truly helpful to all, especially noobs.

Discourse, on the other hand, seems to want to steer noobs to paid services.

That is not a big negative; as everyone has a right to make a living; and I heard the owners of Discourse make a fortune.

1 Like