November Happy Hour will be moved to Thursday December 5th.

Adding meta field programmatically in server farm

Vote:
 

We're experiencing a very specific issue when it comes to programmatically adding meta fields (to purchase orders, in this case), specifically in a deployment phase. Our process is:

  1. Deploy to editorial server in DMZ (regular IIS).
  2. Create deployment slot in Azure and deploy code to it.
  3. Warm up deployment slot (and editorial server). This takes 4-8 minutes.
  4. Swap deployment slot with production one.

When we add a new meta field, that will be added in a initialization module and it will be executed after step 1 in the process above (as soon as the editorial server is hit by a request I guess). The time between step 1 and step 4, where the production slot is replaced, is like 10 minutes.

During this period of time, there's a mismatch when it comes to meta fields in the purchase order meta class. The new meta field is created, and thus the stored procedure mdpsp_avto_OrderGroup_PurchaseOrder_Update is changed to accept the new meta field. However, the production slot is not aware of the new meta field yet so if a purchase order is created during this time - the site will crash.

My theory is that meta class changes should trigger cache to be invalidated in a server farm. Our event replication in the server farm seems to work though. Currently, I believe that the event broker isn't enabled until after all init modules has been executed (see EventsInitialization...InitComplete). That would explain why the meta class cache isn't invalidated on other servers (since probably no events are sent).

Thoughts anyone?

We're adding meta fields to the purchase order meta class like this: https://github.com/episerver/Foundation/blob/c48e99897bf82c117725d2712719ffd9c2a0addb/src/Foundation.Commerce/Extensions/InitializationEngineExtensions.cs#L64.

Edit - I don't exactly recall the error message, other than the meaning being something like "wrong parameters sent to mdpsp_avto_OrderGroup_PurchaseOrder_Update". This is the stack trace:

System.Data.SqlClient.SqlException:
   at System.Data.SqlClient.SqlConnection.OnError (System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning (System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
   at System.Data.SqlClient.TdsParser.TryRun (System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader (System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds (System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader (System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery (System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery (System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
   at EPiServer.Data.Providers.SqlTransientErrorsRetryPolicy.Execute (EPiServer.Data, Version=11.13.0.0, Culture=neutral, PublicKeyToken=8fe83dea738b45b7)
   at Mediachase.Data.Provider.SqlDataProvider.ExecuteNonExec (Mediachase.SqlDataProvider, Version=13.7.0.0, Culture=neutral, PublicKeyToken=41d2e7a615ba286c)
   at Mediachase.MetaDataPlus.Common.DBHelper.ExecuteNonQuery (Mediachase.MetaDataPlus, Version=13.7.0.0, Culture=neutral, PublicKeyToken=41d2e7a615ba286c)
   at Mediachase.MetaDataPlus.Configurator.MetaDataPlusDatabase+StoredProcedure.ExecuteNonQuery (Mediachase.MetaDataPlus, Version=13.7.0.0, Culture=neutral, PublicKeyToken=41d2e7a615ba286c)
   at Mediachase.MetaDataPlus.Configurator.MetaDataPlusDatabase.UpdateMetaObject (Mediachase.MetaDataPlus, Version=13.7.0.0, Culture=neutral, PublicKeyToken=41d2e7a615ba286c)
   at Mediachase.MetaDataPlus.MetaObject.AcceptChanges (Mediachase.MetaDataPlus, Version=13.7.0.0, Culture=neutral, PublicKeyToken=41d2e7a615ba286c)
   at Mediachase.Commerce.Storage.MetaStorageBase.AcceptChanges (Mediachase.Commerce, Version=13.7.0.0, Culture=neutral, PublicKeyToken=6e58b501b34abce3)
   at Mediachase.Commerce.Orders.LineItem.AcceptChanges (Mediachase.Commerce, Version=13.7.0.0, Culture=neutral, PublicKeyToken=6e58b501b34abce3)
   at Mediachase.Commerce.Storage.MetaStorageCollectionBase`1.AcceptChanges (Mediachase.Commerce, Version=13.7.0.0, Culture=neutral, PublicKeyToken=6e58b501b34abce3)
   at Mediachase.Commerce.Orders.OrderForm.AcceptChanges (Mediachase.Commerce, Version=13.7.0.0, Culture=neutral, PublicKeyToken=6e58b501b34abce3)
   at Mediachase.Commerce.Storage.MetaStorageCollectionBase`1.AcceptChanges (Mediachase.Commerce, Version=13.7.0.0, Culture=neutral, PublicKeyToken=6e58b501b34abce3)
   at Mediachase.Commerce.Orders.OrderGroup.AcceptChanges (Mediachase.Commerce, Version=13.7.0.0, Culture=neutral, PublicKeyToken=6e58b501b34abce3)
   at Mediachase.Commerce.Orders.PurchaseOrder.AcceptChanges (Mediachase.Commerce, Version=13.7.0.0, Culture=neutral, PublicKeyToken=6e58b501b34abce3)
   at EPiServer.Commerce.Order.Internal.PurchaseOrderProvider.Save (EPiServer.Business.Commerce, Version=13.7.0.0, Culture=neutral, PublicKeyToken=8fe83dea738b45b7)
   at EPiServer.Commerce.Order.Internal.SerializableCartProvider.SaveAsPurchaseOrder (EPiServer.Business.Commerce, Version=13.7.0.0, Culture=neutral, PublicKeyToken=8fe83dea738b45b7)
   at EPiServer.Commerce.Order.Internal.DefaultOrderRepository.SaveAsPurchaseOrder (EPiServer.Business.Commerce, Version=13.7.0.0, Culture=neutral, PublicKeyToken=8fe83dea738b45b7)
#230143
Edited, Oct 30, 2020 14:28
Vote:
 

Try implementing IMigrationStep instead of an initialization module. It will run only once (unless your migration code fails and you click Retry) and it won't let the site start up until successful.

#230144
Oct 30, 2020 14:43
Andreas J - Oct 30, 2020 15:26
Sounds like a good suggestion. I've never seen meta fields being added that way earlier, which surprises me somewhat. I won't accept your answer just yet, in case someone wants to add their 5 cents :)
Stefan Holm Olsen - Oct 30, 2020 16:09
This is the way Episerver Commerce itself maintain meta fields, whenever needed in an update. But it is not documented on World; only in blog posts.
Vote:
 

Even IMigrationStep is not guaranteed to run in only one instance - although it is guaranteed to run successfully once. THis is something we are looking into.

Also it's an internal API, meaning it can be changed without notice. One can argue such useful API should be public, but it is what it is right now (which is why it's not officially documented)

#230173
Oct 31, 2020 9:55
Andreas J - Nov 01, 2020 10:10
What's your suggestion?

Also, what do you think about my theory that event broker isn't ready when the init module is run (which causes cache invalidation to not work)? Looking at InitComplete in EventsInitialization - that seems to be the case.
Vote:
 

Without looking into the "crash", I can't say anything for sure. But it's possible that not the cache invalidation, simply that production does not have a new code to set the value properly. for example if you add a required field (and update your code to manage that), production does not set that value which blows up when it reaches database. Just a guess by a long shot thought.

 

#230233
Nov 02, 2020 11:41
Andreas J - Nov 02, 2020 11:50
For me, your answer doesn't make sense. Meta fields should not require code at all - just a state in the database. Otherwise, you would not be able to add a meta field in commerce manager and still have the prod site working.
Quan Mai - Nov 02, 2020 11:56
It depends on what the attributes of the metafield. You can make a metafield required, which makes the corresponding column NOT NULL, and it can blow up if your code does not take that into account.
And I don't expect to answer your question, I am just stating a possibility, which I already said "a guess by a long shot". If that does not make senses to you, well, so be it.
Andreas J - Nov 02, 2020 12:05
Didn't think about the not null case, and also, sorry - didn't mean to depreciate your answer!
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.