8 Replies Latest reply on Dec 5, 2018 9:38 AM by Mark Price

    Velocity macros being saved.. globally.. or some dark magic like that

    Stephen Baker

      I couldn't figure out why my macro was not updating on successive calls when trying to update the code. I did find out what was going on... but why it was... ¯\_(ツ)_/¯

       

      Welp, turns out whenever the script token was saved a version of the macro at that time was "created in memory" - or some such thing like that - using the macro name, and any changes to the macro contents were not actually applying to future calls of the macro. I tried the unplug-it-and-plug-it-in-again method on previews, browser windows, etc., to no avail.

       

      Is this how macros are supposed to function? Is there fine print for macros in Marketo?

       

      Here is an example of two tests I made after updating a macro with new code:

       

      Code 1:

      #macro( getCreditUnionNumber3 $list $source )
      #foreach ( $creditUnion in $list )
      #if ( $creditUnion == $source )
      #set ( $phoneNumber = $creditUnion[$source]) )
      #break($getCreditUnionNumber)
      #end
      #set ( $a = $creditUnion )
      #set ( $b = $creditUnion[$source] )
      union = ${a}<br/>
      number = ${b}<br/>
      #end
      #end
      

      Called by: #getCreditUnionNumber3( $phoneList $leadSource )

      Output 2:

      number = ${creditUnion[$velocityCount].value}
      test union = ${creditUnion[$velocityCount].key}
      number = ${creditUnion[$velocityCount].value}
      test union = ${creditUnion[$velocityCount].key}
      number = ${creditUnion[$velocityCount].value}
      test union = ${creditUnion[$velocityCount].key}
      (...)
      

       

      Code 2:

      #macro( getCreditUnionNumber $list $source )
      #foreach ( $creditUnion in $list )
      #if ( $creditUnion == $source )
      #set ( $phoneNumber = $creditUnion[$source]) )
      #break($getCreditUnionNumber)
      #end
      #set ( $a = $creditUnion )
      #set ( $b = $creditUnion[$source] )
      union = ${a}<br/>
      number = ${b}<br/>
      #end
      #end
      

      Called by: #getCreditUnionNumber( $phoneList $leadSource )

      Output 2:

      number = ${b}
      test union = [phone number was here]
      number = ${b}
      test union = [phone number was here]
      number = ${b}
      test union = [phone number was here]
      (...)
      

       

      It might be hard to follow along with all the noise above.

       

      But if you track carefully, you'll see that the macro names changed for the two outputs, but the code contents did not change. However, if you are still following along , the rendered output of the loop for Code 1 is using a previous iteration of the macro "#getCreditUnionNumber3" where I was messing around with key-value pairs. (I had to increment the name to get past the issue and am using v3 for the example.) You should see that the current macros in Code 1 and Code 2 do not reference keys or values anywhere.

       

      Btw, the code I was using was busted which was why literal VTL was being printed. That was resolved, don't think it's related.

       

      The solution I found was renaming the macro every time I made a change to the macro contents.

        • Re: Velocity macros being saved.. globally.. or some dark magic like that
          Sanford Whiteman

          Yes, Velocimacros are cached by the engine (this is the norm in production Velocity installs). Indeed, versioning the name is the way to go.

           

          I have a couple more things to add on this... back later.

          2 of 2 people found this helpful
          • Re: Velocity macros being saved.. globally.. or some dark magic like that
            Sanford Whiteman

            OK, a few more thoughts on Velocimacros.  I've got a short blog post coming out about them, but I'm not worried about redundancy since only you and Mark are probably reading this thread.

             

            As I said, VMs are cached until the Velocity engine context and/or whole JVM restarts (for practical purposes in Marketo, these are both "forever," I just want to be precise).  So your experience is expected, even if not documented by Marketo themselves it is in the Velocity docs (OK, it could be clearer even in the docs, but it gets at least a passing mention). You have to change the name of the macro, you can't ever "recompile" it. For this reason I start out by appending _v1 to any #macro name.

             

            On the wider level, you should note that unless you're storing a ton of code in a Velocimacro (think 500+ LOC), the macro actually executes more slowly than the exact same code used inline.

             

            Now, when I say "more slowly" I mean you'd only notice it over the course of 100,000+ executions, but you do take a ~10-20% hit. This surprised me at first because you might think with the code being precompiled to Velocity's version of bytecode and then cached in that form, it couldn't help but be faster, or at least the same. But it turns out the additional function call overhead more than counterbalances the caching efficiency. Go figure.

             

            So the reasons -- and these are extremely valid ones! -- to use macros are only encapsulation* better programming style, DRY principle, etc. If you're only executing the macro once, in only one token, perhaps it doesn't need to be a macro at all. What I'd like to see is the actual source data you're processing here (the $list and $source variables). This code is quite strange looking:

             

            #if( $creditUnion == $source )
            #set( $phoneNumber = $creditUnion[$source] )
            

             

            If $list is an ArrayList and $creditUnion is a Hash (LinkedHashMap) and you're trying to find which object in the list is strictly equal to some reference object ($source) that much makes sense.

             

            But then $source is also a Hash, and $creditUnion[$source] is a circular reference. It's impossible to get a circular reference injected into Velocity by Marketo. So you'd have to be creating it yourself, which is... a terrible idea.

             

             

             

            * real encapsulation being nonexistent in VTL due to the global namespace, but close enough

            2 of 2 people found this helpful