tag:blogger.com,1999:blog-8393573685407510852024-02-22T07:51:41.334-08:00Melo's blog - Batteries not includedGuilherme Melohttp://www.blogger.com/profile/04196746370820421715noreply@blogger.comBlogger6125tag:blogger.com,1999:blog-839357368540751085.post-90765737855917809872012-09-30T12:22:00.000-07:002012-09-30T12:22:00.216-07:00Coherence Cluster Capacity<b id="internal-source-marker_0.16307334112934768" style="font-weight: normal;"><span style="font-family: Times, Times New Roman, serif;"><span style="vertical-align: baseline; white-space: pre-wrap;">Hello!, Understanding the capacity of a cluster is very important, as an overloaded cluster will not perform well.</span><br /><span style="vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="vertical-align: baseline; white-space: pre-wrap;">The overall capacity of a cluster is determined by how many members it has, two physical resources determine the number of members that can be available at a given time, memory and cpu cores.</span><br /><span style="vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="font-weight: bold; vertical-align: baseline; white-space: pre-wrap;">CPU Considerations:</span><br /><span style="vertical-align: baseline; white-space: pre-wrap;">It is important to understand that coherence is a series of services running in a jvm and these </span><span style="background-color: white; vertical-align: baseline; white-space: pre-wrap;">services are asynchronous named threads, all of which require CPU cycles to work, as does the JVM, and the OS, so, when determining your capacity it is very important to remember that each member will increase this overhead, limiting the number of members per box.</span><br /><span style="background-color: white; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="font-weight: bold; vertical-align: baseline; white-space: pre-wrap;">Memory Considerations:</span><span style="vertical-align: baseline; white-space: pre-wrap;">When deciding the amount of data it is vital to remember that not all of the heap space in the storage node is available for primary storage, in fact, most of it is not. There are several overheads that need to be accounted for:</span></span></b><br />
<ul style="margin-bottom: 0pt; margin-top: 0pt;">
<li style="list-style-type: disc; vertical-align: baseline;"><b id="internal-source-marker_0.16307334112934768" style="font-weight: normal;"><span style="vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: Times, Times New Roman, serif;">For Partitioned caches each piece of data is backed up on a different JVM, therefore we must account for the size of twice the serialized object size per Object.</span></span></b></li>
<li style="list-style-type: disc; vertical-align: baseline;"><b id="internal-source-marker_0.16307334112934768" style="font-weight: normal;"><span style="vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: Times, Times New Roman, serif;">For Replicated caches the objects are kept unserialized after the first access, and are present in every member and must be sized accordingly.</span></span></b></li>
<li style="list-style-type: disc; vertical-align: baseline;"><b id="internal-source-marker_0.16307334112934768" style="font-weight: normal;"><span style="vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: Times, Times New Roman, serif;">The Keys of the objects and Indexed data are kept unserialized.</span></span></b></li>
<li style="list-style-type: disc; vertical-align: baseline;"><b id="internal-source-marker_0.16307334112934768" style="font-weight: normal;"><span style="vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: Times, Times New Roman, serif;">Every Named Cache has an overhead of 250kb</span></span></b></li>
<li style="list-style-type: disc; vertical-align: baseline;"><b id="internal-source-marker_0.16307334112934768" style="font-weight: normal;"><span style="vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: Times, Times New Roman, serif;">Each entry has an overhead of 144 bytes.</span></span></b></li>
<li style="list-style-type: disc; vertical-align: baseline;"><b id="internal-source-marker_0.16307334112934768" style="font-weight: normal;"><span style="font-family: Times, Times New Roman, serif;"><span style="vertical-align: baseline; white-space: pre-wrap;">Coherence needs some </span><span style="font-style: italic; vertical-align: baseline; white-space: pre-wrap;">scrap space</span><span style="vertical-align: baseline; white-space: pre-wrap;">; that is, space to unserialize and work with objects, when executing listeners, entry processors and aggregators.</span></span></b></li>
<li style="list-style-type: disc; vertical-align: baseline;"><b id="internal-source-marker_0.16307334112934768" style="font-weight: normal;"><span style="vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: Times, Times New Roman, serif;">The JVM has its own overhead and to perform optimally should not be executed over 75 to 80% capacity. </span></span></b></li>
</ul>
<b id="internal-source-marker_0.16307334112934768" style="font-weight: normal;"><span style="font-family: Times, Times New Roman, serif;"><span style="vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="font-weight: bold; vertical-align: baseline; white-space: pre-wrap;">Sizing your members:</span><br /><span style="vertical-align: baseline; white-space: pre-wrap;">Taking all of those overheads into consideration, we can start to size our members, the sum of whom will be our total capacity.</span><br /><ul style="margin-bottom: 0pt; margin-top: 0pt;">
<li style="list-style-type: disc; vertical-align: baseline;"><span style="vertical-align: baseline; white-space: pre-wrap;">¼ of the capacity should remain unused to ensure optimal performance by the JVM.</span></li>
<li style="list-style-type: disc; vertical-align: baseline;"><span style="vertical-align: baseline; white-space: pre-wrap;">¼ of the capacity should not be used for primary storage and will be used for the overheads Coherence needs.</span></li>
<li style="list-style-type: disc; vertical-align: baseline;"><span style="vertical-align: baseline; white-space: pre-wrap;">This leaves us with ½ of the JVM to use as storage in to be used in a mix of Partitioned and Replicated storage, however, it is important to take into consideration that:</span></li>
<ul style="margin-bottom: 0pt; margin-top: 0pt;">
<li style="list-style-type: circle; vertical-align: baseline;"><span style="vertical-align: baseline; white-space: pre-wrap;">For Partitioned caches you must consider twice the size of the pof serialized object, as it will be stored twice.</span></li>
<li style="list-style-type: circle; vertical-align: baseline;"><span style="vertical-align: baseline; white-space: pre-wrap;">For Replicated caches you must take into account the native size of the objects.</span></li>
</ul>
</ul>
<span style="vertical-align: baseline; white-space: pre-wrap;">Taking this into account once you know the size of your objects it becomes trivial to determine how much data you can store per storage node.</span><br /><span style="vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="font-weight: bold; vertical-align: baseline; white-space: pre-wrap;">Enforcing your cache sizing:</span><br /><span style="vertical-align: baseline; white-space: pre-wrap;">In order to ensure that coherence enforces your cache sizing you must specify on the backing map of each cache Binary as the unit calculator, the maximum size in bytes of primary storage for that cache (remembering that for Partitioned caches the actual memory spent spent is twice that due to the backup) and the eviction strategy.</span><br /><span style="vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="font-weight: bold; vertical-align: baseline; white-space: pre-wrap;">The dangers of wildcards:</span><span style="vertical-align: baseline; white-space: pre-wrap;">As explained above, the sizing is performed per cache, so wildcards break this contract and if you are using wildcards there is no way to directly enforce a sizing policy.</span><br /><span style="vertical-align: baseline; white-space: pre-wrap;">For instance, if you have a cache mapping of my-cache-* and set a high units count to 100MB. When you create a cache named my-cache-one, the maximum amount of data that can be added is 100MB. The problem is you can create an unlimited number of caches using that mapping, and each of them will allow 100MB, therefore, potentially resulting in a out of memory exception. </span><br /><span style="vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="font-weight: bold; vertical-align: baseline; white-space: pre-wrap;">Sizing your cluster:</span><br /><span style="vertical-align: baseline; white-space: pre-wrap;">Based on these considerations we can determine how many storage nodes we will need to store the data required by applying a simple formula where x is the number of nodes required:</span></span></b><br />
<b style="font-weight: normal;"><span style="vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span></b>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOyxuOARh3MIBZ5yGbD19zjPEFxJATgUjZ89AHo1GMNMhgzkE0Dmgm6pf0sEcLwMuy8jw1kW3rMwWaEDCL5mAZ6M0_Jn8nnrn0yTXuya47hnQ2kJ_ncLUpJB6dM6O1tx2c15AqItv1-Mc/s1600/Screen+shot+2012-09-30+at+20.03.14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: Times, Times New Roman, serif;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOyxuOARh3MIBZ5yGbD19zjPEFxJATgUjZ89AHo1GMNMhgzkE0Dmgm6pf0sEcLwMuy8jw1kW3rMwWaEDCL5mAZ6M0_Jn8nnrn0yTXuya47hnQ2kJ_ncLUpJB6dM6O1tx2c15AqItv1-Mc/s1600/Screen+shot+2012-09-30+at+20.03.14.png" /></span></a></div>
<b style="font-weight: normal;"><span style="vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span></b>
<b id="internal-source-marker_0.16307334112934768" style="font-weight: normal;"><span style="font-family: Times, Times New Roman, serif;"><span style="vertical-align: baseline; white-space: pre-wrap;">Which in essence means that you must add the size of each object that you aim to place into a distributed cache and multiply it by the number of objects in that cache, multiply it by two to account for the backups, and divide that by the storage capacity of the JVM, which is, as discussed in the previous session half of the JVM size, minus the size times the number of objects of each replicated cache, as they will take up space in every JVM.</span><br /><span style="vertical-align: baseline; white-space: pre-wrap;">After calculating the number of members that are necessary, you must add overhead to account for the loss of one physical machine, so, if each physical machine holds 3 members, you must add 3.</span><br /><span style="vertical-align: baseline; white-space: pre-wrap;">For instance, if you had three distributed caches, one with 100,000 5k objects, 50,000 1Mb objects, and 1,000,000 2k objects and one replicated cache with 4,000 50k objects, and we are hosting 3 processes per physical machines making the calculation:</span></span></b><br />
<b style="font-weight: normal;"><span style="vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span></b>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_g7BP9rsFahN8DUtLi1-ekAcEGXsiCYAp8T6Wv7M7S0M2ekD0zKY1CpEqhbxxLU32pIDrDoL8fHbZ-xmfiljcYhkHeSjm24kfTP_VB7iVvVDGlpxiV5g-7vPQbqhxLgK3Kteplnl9-Jg/s1600/Screen+shot+2012-09-30+at+20.05.17.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: Times, Times New Roman, serif;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_g7BP9rsFahN8DUtLi1-ekAcEGXsiCYAp8T6Wv7M7S0M2ekD0zKY1CpEqhbxxLU32pIDrDoL8fHbZ-xmfiljcYhkHeSjm24kfTP_VB7iVvVDGlpxiV5g-7vPQbqhxLgK3Kteplnl9-Jg/s1600/Screen+shot+2012-09-30+at+20.05.17.png" /></span></a></div>
<b style="font-weight: normal;"><span style="vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span></b>
<b id="internal-source-marker_0.16307334112934768" style="font-weight: normal;"><span style="vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: Times, Times New Roman, serif;">next we convert all to the same unit and get our final value:</span></span></b><br />
<b style="font-weight: normal;"><span style="vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span></b>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyHCHSTVpTPXFbMSsnvrnODMZsLpGhIJ97dYsVzAhaLSjesJuNm5O3l0X0h5eN1LXwm7OSJk9kQz3r6-FiP9jf9kfKst-UvnTXnirUuey6pTAnFXBq-JHnb1kM_51yP5ScnkGWsnPewUw/s1600/Screen+shot+2012-09-30+at+20.06.23.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: Times, Times New Roman, serif;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyHCHSTVpTPXFbMSsnvrnODMZsLpGhIJ97dYsVzAhaLSjesJuNm5O3l0X0h5eN1LXwm7OSJk9kQz3r6-FiP9jf9kfKst-UvnTXnirUuey6pTAnFXBq-JHnb1kM_51yP5ScnkGWsnPewUw/s1600/Screen+shot+2012-09-30+at+20.06.23.png" /></span></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: Times, Times New Roman, serif;"><br /></span></div>
<b id="internal-source-marker_0.16307334112934768" style="font-weight: normal;"><span style="font-family: Times, Times New Roman, serif;"><span style="vertical-align: baseline; white-space: pre-wrap;">so, now we know we will need 35 storage enabled members to store the data.</span><br /><span style="vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="font-weight: bold; vertical-align: baseline; white-space: pre-wrap;">Calculating the number of Members per physical server:</span><br /><span style="vertical-align: baseline; white-space: pre-wrap;">Once the number of members has been determined, you can decide how many members will be in each physical box, however there are some more things to consider, the server hosting the member still needs memory and cycles for the OS to work optimally, and paging/swapping should never be used, so we need to make sure not to use all of the available physical memory with Coherence members.</span><br /><span style="vertical-align: baseline; white-space: pre-wrap;">The CPU usage, as explained above is also very important and if not correctly managed performance will be greatly degraded due to threads fighting over cores.</span><span style="background-color: white; vertical-align: baseline; white-space: pre-wrap;">The exact number of members per core will depend on the load, but one core per member is a reasonable starting number, but this should be profiled and tuned by running tests and checking the task backlog Mbean and the CPU usage in the OS, making sure that it is not running too hot or too slow, and adjusting the number of members per box accordingly.</span><br /><span style="background-color: white; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="background-color: white; font-weight: bold; vertical-align: baseline; white-space: pre-wrap;">Calculating the number of extend members per cluster</span><br /><span style="background-color: white; vertical-align: baseline; white-space: pre-wrap;">Members running the Coherence*Extend service should be storage disabled and should have JVM’s the same size as the other members, because, even though they will not store data they will potentially be doing multiple aggregations for different clients. It is important to remember that you will need one Thread per active concurrent connection by setting the Thread-count on the proxy configuration, and should derive the number of proxies you need from that, keeping in mind the cpu considerations discussed earlier and that you should at the very least have two on different physical machines for reliability. </span></span></b><br />
<b style="font-weight: normal;"><span style="background-color: white; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span></b>
<b style="font-weight: normal;"><span style="background-color: white; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: Times, Times New Roman, serif;">Cheers,</span></span></b><br />
<b style="font-weight: normal;"><span style="font-family: Arial; font-size: 16px; vertical-align: baseline; white-space: pre-wrap;"><br /></span></b>Guilherme Melohttp://www.blogger.com/profile/04196746370820421715noreply@blogger.com6London, UK51.5073346 -0.127683151.3492066 -0.4435401 51.6654626 0.1881739tag:blogger.com,1999:blog-839357368540751085.post-80473111206046440882012-08-25T07:36:00.000-07:002012-08-29T07:43:33.332-07:00Entry Processors part 2 - Deadlocking<!--[if gte mso 9]><xml>
<o:DocumentProperties>
<o:Template>Normal.dotm</o:Template>
<o:Revision>0</o:Revision>
<o:TotalTime>0</o:TotalTime>
<o:Pages>1</o:Pages>
<o:Words>57</o:Words>
<o:Characters>329</o:Characters>
<o:Company>Kent U</o:Company>
<o:Lines>2</o:Lines>
<o:Paragraphs>1</o:Paragraphs>
<o:CharactersWithSpaces>404</o:CharactersWithSpaces>
<o:Version>12.0</o:Version>
</o:DocumentProperties>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
</o:OfficeDocumentSettings>
</xml><![endif]--><!--[if gte mso 9]><xml>
<w:WordDocument>
<w:Zoom>0</w:Zoom>
<w:TrackMoves>false</w:TrackMoves>
<w:TrackFormatting/>
<w:PunctuationKerning/>
<w:DrawingGridHorizontalSpacing>18 pt</w:DrawingGridHorizontalSpacing>
<w:DrawingGridVerticalSpacing>18 pt</w:DrawingGridVerticalSpacing>
<w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery>
<w:DisplayVerticalDrawingGridEvery>0</w:DisplayVerticalDrawingGridEvery>
<w:ValidateAgainstSchemas/>
<w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid>
<w:IgnoreMixedContent>false</w:IgnoreMixedContent>
<w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText>
<w:Compatibility>
<w:BreakWrappedTables/>
<w:DontGrowAutofit/>
<w:DontAutofitConstrainedTables/>
<w:DontVertAlignInTxbx/>
</w:Compatibility>
</w:WordDocument>
</xml><![endif]--><!--[if gte mso 9]><xml>
<w:LatentStyles DefLockedState="false" LatentStyleCount="276">
</w:LatentStyles>
</xml><![endif]-->
<!--[if gte mso 10]>
<style>
/* Style Definitions */
table.MsoNormalTable
{mso-style-name:"Table Normal";
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:12.0pt;
font-family:"Times New Roman";
mso-ascii-font-family:Cambria;
mso-ascii-theme-font:minor-latin;
mso-fareast-font-family:"Times New Roman";
mso-fareast-theme-font:minor-fareast;
mso-hansi-font-family:Cambria;
mso-hansi-theme-font:minor-latin;
mso-bidi-font-family:"Times New Roman";
mso-bidi-theme-font:minor-bidi;
mso-ansi-language:EN-US;}
</style>
<![endif]-->
<!--StartFragment-->
<br />
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;">This is Part two
of my entry processor post and deals with the advantages and disadvantages of
cross cache entry processors.<o:p></o:p></span></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b><span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;">Cross cache
Entry processors<o:p></o:p></span></span></b></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;">As mentioned in
the previous post entry processors allow us to update values in the most efficient
manner, however, often you will want to update values in multiple caches, </span></span><span style="font-family: Times, 'Times New Roman', serif;">or update a value that is derived from related entries,</span><span style="font-family: Times, 'Times New Roman', serif;"> to do this, all you would need to do is a call from CacheFactory.getCache, from inside the entry processor, for instance:</span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span></div>
<div class="MsoNormal">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5oM8wzB_vLsCJI4tucQ_XGEUWkcTkWkDvm6s49VUurmLmLAzrxwocnGeUkgnNLhiE8H7qluejvnHvNQviN2qEcl-RGwpzEasvp9-S5eUkT61sxWZX6x7cggdfW07eXhjRs-DTsnklAO0/s1600/cross-cache.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5oM8wzB_vLsCJI4tucQ_XGEUWkcTkWkDvm6s49VUurmLmLAzrxwocnGeUkgnNLhiE8H7qluejvnHvNQviN2qEcl-RGwpzEasvp9-S5eUkT61sxWZX6x7cggdfW07eXhjRs-DTsnklAO0/s1600/cross-cache.jpg" /></a></div>
<span style="font-family: Times, Times New Roman, serif;">By looking at
the diagram we can see the problem straight away, as our code will perform an
extra network hop to get one of the blue objects, nothing guarantees the object
will be within the same node. So how can we solve this?</span><br />
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;">Coherence offers
something called key association, which allows us to ensure that two or more
related entries that share the same business key are co-located, this will require for both caches to be sharing a service. </span></span><span style="font-family: Times, 'Times New Roman', serif;">I have previously shown some benefits of key
association in the versioning </span><a href="http://www.gmelo.org/2012_05_01_archive.html" style="font-family: Times, 'Times New Roman', serif;" target="_blank">post</a><b style="font-family: Times, 'Times New Roman', serif;">, </b><span style="font-family: Times, 'Times New Roman', serif;">and the documentation is <a href="http://docs.oracle.com/cd/E24290_01/coh.371/e22837/api_dataaffinity.htm#BABFEBHB" target="_blank">here</a>, so,
I’ll assume everyone is familiar with it, if not, take a look at the mechanism
and i’ll wait and check my emails.</span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;">Welcome back,
now, after we implement key association our cache would look like this:</span><o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigahROu2IUoQACqZFp6_9mCeBIeMgpNBTclap3RyhSXlOo_k05D-mXqTK57MjPkmZwwyVbQA86cbHxj31jh5wLx5NDulsaM8vksfNQk1siAgEye76pw0eAhCOo3B4z62VaQvNCQuH7vXg/s1600/cross-cache-key-assossiation.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigahROu2IUoQACqZFp6_9mCeBIeMgpNBTclap3RyhSXlOo_k05D-mXqTK57MjPkmZwwyVbQA86cbHxj31jh5wLx5NDulsaM8vksfNQk1siAgEye76pw0eAhCOo3B4z62VaQvNCQuH7vXg/s1600/cross-cache-key-assossiation.jpg" /></a></div>
<div class="MsoNormal">
<span style="font-family: Times, 'Times New Roman', serif;">Which is exactly
what we wanted, but when we run the code we get this exception:</span></div>
<div class="MsoNormal">
<span lang="EN-US" style="color: red; font-family: Times, Times New Roman, serif;"><span style="font-size: x-small;">2012-08-07 11:49:41,510 ERROR [Logger@9252516
3.7.1.0] Coherence(3) - 2012-08-07 11:49:41.507/14.742 Oracle Coherence GE
3.7.1.0 <Error> (thread=DistributedCache:first-service, member=1):
Assertion failed: poll() is a blocking call and cannot be called on the Service
thread</span><o:p></o:p></span></div>
<div class="MsoNormal">
<span style="font-family: Times, Times New Roman, serif;"><br /></span></div>
<div class="MsoNormal">
<span style="font-family: Times, Times New Roman, serif;"><span lang="EN-US">This is very
dangerous, because if we have multiple threads for the service instead of an
exception you will get is the warning:<br />
</span><span lang="EN-US" style="color: red; font-size: x-small;">2012-08-07
11:40:50,040 WARN [Logger@9243192
3.7.1.0] Coherence(3) - 2012-08-07 11:40:50.040/5.833 Oracle Coherence GE
3.7.1.0 <Warning> (thread=first-serviceWorker:1, member=1): Application
code running on "first-service" service thread(s) should not call
ensureCache as this may result in deadlock. The most common case is a CacheFactory
call from a custom CacheStore implementation.</span><span lang="EN-US"><o:p></o:p></span></span></div>
<div class="MsoNormal">
<span style="font-family: Times, Times New Roman, serif;"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="font-family: Times, Times New Roman, serif;">This happens because you are accessing the service from a thread from the same service, and here is
where the behavior starts getting tricky, as if you have two or more free
threads it will work as expected, barring the exception, however if you only
have one free thread or only one extra thread on your service, the execution of
the entry processor will deadlock until being killed by the service guardian.
You should never do this, so how should we go cross cache properly?<o:p></o:p></span></div>
<div class="MsoNormal">
<span style="font-family: Times, Times New Roman, serif;"><br /></span></div>
<div class="MsoNormal">
<b><span lang="EN-US" style="font-family: Times, Times New Roman, serif;">Direct
backing map cross cache access <o:p></o:p></span></b></div>
<div class="MsoNormal">
<span lang="EN-US" style="font-family: Times, Times New Roman, serif;">As the title,
which is a mouthful, explains we should access the entires directly in the
backing. Because we are using key association we are sure the entries will be
co-located, as explained in figure 2.</span><br />
<br />
<script src="https://gist.github.com/3465427.js"> </script>
<span style="font-family: Times, Times New Roman, serif;"><span lang="EN-US"><br /></span>
</span><br />
<div class="MsoNormal">
<b><span lang="EN-US" style="font-family: Times, Times New Roman, serif;">Entry Processors
in coherence 3.7<o:p></o:p></span></b></div>
<div class="MsoNormal">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglHW-GaUYpjOUVcD7b5nHCg5-cIo_bq0Pb6tNOgaEvc-NOSwgLwlC43FbTXXGVzTMBlZpNwuIFtP5_jBa971QGb1Qe_2g_d0PXJWzmGdHkM2ylTGCs1F8T2bzvzHR6hRudrJB1ExRIli8/s1600/cross-cache-processor-large.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglHW-GaUYpjOUVcD7b5nHCg5-cIo_bq0Pb6tNOgaEvc-NOSwgLwlC43FbTXXGVzTMBlZpNwuIFtP5_jBa971QGb1Qe_2g_d0PXJWzmGdHkM2ylTGCs1F8T2bzvzHR6hRudrJB1ExRIli8/s1600/cross-cache-processor-large.jpg" /></a></div>
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span>
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span>
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span>
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span>
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span>
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span>
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span>
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span>
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span>
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span>
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span>
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span>
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span>
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span>
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span>
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span>
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span>
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span>
<span lang="EN-US"><span style="font-family: Times, Times New Roman, serif;">Entry Processors
have gotten better in 3.7, as its transactionality and locking has been
extended to include cross cache access, so all of the wonderful transactional
guarantees and locking explained in my previous post are extended to cross
cache access. This however creates quite a big problem, as you can potentially
deadlock your application. It might not be clear at first glance, but the
diagram below should make it clearer:</span><span style="font-family: Times, 'Times New Roman', serif; text-align: center;"><br /></span></span><br />
<div style="text-align: start;">
<span lang="EN-US"><span style="font-family: Times, 'Times New Roman', serif; text-align: center;">As all access
locks, the following example will deadlock and </span><b style="font-family: Times, 'Times New Roman', serif; text-align: center;">the entry processor that was
invoked last will fail due to deadlocking while the one who arrived first will
continue its execution.</b><span style="text-align: center;"><span style="font-family: Times, Times New Roman, serif;"> This is a deceptively simple problem to avoid with
good design… Creating and documenting the expected flow through the caches- i.e. all cross cache entry processors
should go from the first to the second cache, this way they will queue and
provide the expected behavior from entry processors.</span></span></span><br />
<span lang="EN-US"><span style="text-align: center;"><span style="font-family: Times, Times New Roman, serif;"><br /></span></span></span>
<span lang="EN-US"><span style="text-align: center;"><span style="font-family: Times, Times New Roman, serif;">Cheers,</span></span></span></div>
</div>
<!--EndFragment--></div>
<!--EndFragment--></div>
Guilherme Melohttp://www.blogger.com/profile/04196746370820421715noreply@blogger.com3London, UK51.5073346 -0.127683151.3492066 -0.4435401 51.6654626 0.1881739tag:blogger.com,1999:blog-839357368540751085.post-55083808359910604502012-08-21T04:13:00.002-07:002012-08-25T08:00:48.589-07:00Entry Processors - Part 1<span style="font-family: Times, 'Times New Roman', serif;">Hello! This is the first of a two part series on entry processors:</span><br />
<span style="font-family: Times, Times New Roman, serif;">Entry processors are a very powerful tool in our coherence toolbox Coherence 3.7 improved the entry processors API even further. In case you haven’t looked at the documentation, below is a quick description:</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<b><span style="font-family: Times, Times New Roman, serif;">Entry Processors:</span></b><br />
<span style="font-family: Times, Times New Roman, serif;">Entry Processors, process entries. I say this because I cannot be the only one who interpreted the name as something that processes things as they enter, which is the job of the trigger, but I digress.</span><br />
<span style="font-family: Times, Times New Roman, serif;">Entry Processors allow us to execute code server side against entries or sets of entries, either passed as a parameter or that match a filter that has been passed as a parameter to the processor. This is the preferred method of updating the value of entries in a cache, as it will be performed in parallel in each node, an implicit lock is acquired in the object and multiple entry processors changing the same object are queued, guaranteeing ordering of updates thus ensuring data consistency.</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<b><span style="font-family: Times, Times New Roman, serif;">Why use entry processors?</span></b><br />
<span style="font-family: Times, Times New Roman, serif;">The main reason is because coherence is a hashmap, so all the operations are put/gets, which means that it is quite easy to end up with an inconsistent state. The diagram below explains the most common scenario.</span><br />
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<br />
<div class="MsoNormal">
<span lang="EN-US" style="font-family: Times, Times New Roman, serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1ljg_npZdmjnR_OcFO3H3u4RWb-HUvcOJjRfPUkC-4Oow2tBYn40IYJ2rvDmWXpBA_NpFXlGledcoUtMSK74MjtWGi4CsyEQJizeXal4oTz7lTwmr_KoLNCboOgwD7rxV71rhMc3VCUY/s1600/Untitled1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: Times, Times New Roman, serif;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1ljg_npZdmjnR_OcFO3H3u4RWb-HUvcOJjRfPUkC-4Oow2tBYn40IYJ2rvDmWXpBA_NpFXlGledcoUtMSK74MjtWGi4CsyEQJizeXal4oTz7lTwmr_KoLNCboOgwD7rxV71rhMc3VCUY/s1600/Untitled1.jpg" /></span></a></div>
<div class="MsoNormal">
<span lang="EN-US" style="font-family: Times, Times New Roman, serif;">
</span></div>
<div class="MsoNormal">
<span lang="EN-US" style="font-family: Times, Times New Roman, serif;">
</span></div>
<div class="MsoNormal" style="margin-left: 36pt; text-indent: -18pt;">
<span style="font-family: Times, Times New Roman, serif;"><span lang="EN-US">●
</span><span lang="EN-US">At T0 the object has its fields
with a value of “Initial” and “Initial”.<o:p></o:p></span></span></div>
<div class="MsoNormal" style="margin-left: 36pt; text-indent: -18pt;">
<span style="font-family: Times, Times New Roman, serif;"><span lang="EN-US">●
</span><span lang="EN-US">At T4 The first field has a
value of “changed” and the second one of “initial”.<o:p></o:p></span></span></div>
<div class="MsoNormal" style="margin-left: 36pt; text-indent: -18pt;">
<span style="font-family: Times, Times New Roman, serif;"><span lang="EN-US">●
</span><span lang="EN-US">At T6 the object will have the
values of “Initial” and “changed”.<br /><br /><o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="font-family: Times, Times New Roman, serif;">From this we can
see that the changes made by the client 1 where overridden by client 2 because
he had a stale version of the object.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="font-family: Times, Times New Roman, serif;">The simplest and
heavy handed way to solve this is:<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="font-family: Times, Times New Roman, serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhc56WOEUTba_WCxm_OluxBvF_vcEWhYOd8G1KkN7-pFz4pX-1cC4kQhWwKb4mOAtJ9R4Ua7PYIE1_a7V2PpNRjf15xlnT9fhQkyNwryx_YJ0f-I2ySQcfEOTPxGtz8XXBuP9IoQVSmuao/s1600/Untitled2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: Times, Times New Roman, serif;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhc56WOEUTba_WCxm_OluxBvF_vcEWhYOd8G1KkN7-pFz4pX-1cC4kQhWwKb4mOAtJ9R4Ua7PYIE1_a7V2PpNRjf15xlnT9fhQkyNwryx_YJ0f-I2ySQcfEOTPxGtz8XXBuP9IoQVSmuao/s1600/Untitled2.jpg" /></span></a></div>
<div class="MsoNormal">
<span lang="EN-US" style="font-family: Times, Times New Roman, serif;"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="font-family: Times, Times New Roman, serif;">
</span></div>
<div class="MsoNormal" style="margin-left: 36pt; text-indent: -18pt;">
<span style="font-family: Times, Times New Roman, serif;"><span lang="EN-US">●
</span><span lang="EN-US">Before actually getting the
objects the clients attempt to get a lock on them.<o:p></o:p></span></span></div>
<div class="MsoNormal" style="margin-left: 36pt; text-indent: -18pt;">
<span style="font-family: Times, Times New Roman, serif;"><span lang="EN-US">●
</span><span lang="EN-US">At T3 client two will ask for a
lock, this will either return false, or he will block until it is released if
he asked for the lock passing a long for the maximum time to wait for the lock
(this will avoid multiple network calls to attempt to get the lock, but you
must set a value smaller than the service guardian, or risk it killing your
Thread.)<o:p></o:p></span></span></div>
<div class="MsoNormal" style="margin-left: 36pt; text-indent: -18pt;">
<span style="font-family: Times, Times New Roman, serif;"><span lang="EN-US">●
</span><span lang="EN-US">At T7 Client 2 finally has the
lock and can get the object.<o:p></o:p></span></span></div>
<div class="MsoNormal">
<span style="font-family: Times, Times New Roman, serif;"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="font-family: Times, Times New Roman, serif;">This will
protect you from stale data, however, we are not doing government work here,
our implementations shouldn’t only work, but they need to be efficient and easy
to maintain and this solution has quite a few problems, for starters it slows
the cache as a whole, as the clients are constantly locking the data. Secondly,
there are extra network hops required for each locking step, further slowing
down the application.</span></div>
<div class="MsoNormal">
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<b><span style="font-family: Times, Times New Roman, serif;">Simple Entry Processor solution:</span></b><br />
<span style="font-family: Times, Times New Roman, serif;">In order to solve this problem when writing a processor we need to ensure it is a simple as possible and that code is deployed on the member that holds the data, as the entry processor will be serialized and sent over the wire. Below is the example code for the firstField entry processor, the one for the second field looks exactly the same, except it changes a different value. In production it would be more efficient to use a POF updater, like the one used in the Versioning post, but I wanted to keep this example as simple as possible.</span></div>
<span style="font-family: Times, Times New Roman, serif;"><br /></span>
<script src="https://gist.github.com/3414539.js"> </script><span style="font-family: Times, Times New Roman, serif;">
And the graph:</span><br />
<div class="MsoNormal">
<span style="font-family: Times, Times New Roman, serif;"><span lang="EN-US"><span lang="EN-US" style="line-height: 115%;"><span style="line-height: 115%;"><br /></span></span></span>
</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgA1w0A39IrYnhBSWa0LxDKIjT0Y0NtqWfMSlPY_Y3MMwI2xpZK3uxGH16mRcsGJHnbGKjZO0RuieWohXVlftEz38vKuQV-mI7bP9uI64DnULbXQlEI7uxfmYYbK5hx9PVdri2r0yTHg5c/s1600/Untitled3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: Times, Times New Roman, serif;"><img border="0" height="247" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgA1w0A39IrYnhBSWa0LxDKIjT0Y0NtqWfMSlPY_Y3MMwI2xpZK3uxGH16mRcsGJHnbGKjZO0RuieWohXVlftEz38vKuQV-mI7bP9uI64DnULbXQlEI7uxfmYYbK5hx9PVdri2r0yTHg5c/s400/Untitled3.jpg" width="400" /></span></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: Times, Times New Roman, serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<div class="MsoNormal" style="margin-left: 36pt; text-indent: -18pt;">
<span style="font-family: Times, Times New Roman, serif;"><span lang="EN-US">●
</span><span lang="EN-US">If the entry processor at T2
arrives before T1 it will get queued to be executed after the lock is released,
as entry processors take an implicit lock on the objects.<o:p></o:p></span></span></div>
<div class="MsoNormal" style="margin-left: 36pt; text-indent: -18pt;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Times, Times New Roman, serif;">Hopefully this has made the advantages of using entry processors clear.</span></div>
</div>
<div class="MsoNormal">
<b><span lang="EN-US" style="font-family: Times, Times New Roman, serif;">Entry
processor Particulars:<o:p></o:p></span></b></div>
<div class="MsoNormal">
<span lang="EN-US" style="font-family: Times, Times New Roman, serif;">As explained
above, locking is very important for the correct functioning of the entry
processor. I found the documentation a bit confusing in this regard, the
description is: “<span style="background: #FEFEFE; mso-pattern: solid white; mso-shading: white;">EntryProcessors are individually executed atomically, however
multiple EntryProcessor invocations by using InvocableMap.invokeAll() do not
execute as one atomic unit.” A
simpler way I found to think about it, is that each group of keys will be
executed atomically, and each group of keys will lock for the entire duration
of that group of keys, for instance, lets say our invokeAll evaluates to keys
1,3,4,7 and 9. For those purposes, it is inconsequential if it evaluates to
those keys through a filter or if you passed in a set of keys. The partition
that contains keys 1,3 and 7 are on member 1 and the ones with keys 4 and 9 are
on member 2. Therefore the changes made on each of those groups will lock and
be transactional for the duration of the execution of all of the keys on that
member.</span><o:p></o:p></span></div>
<div class="MsoNormal">
<span style="font-family: Times, Times New Roman, serif;"><br /></span></div>
<div class="MsoNormal">
<b><span lang="EN-US" style="font-family: Times, Times New Roman, serif;">Entry
Processors on a replicated cache:<o:p></o:p></span></b></div>
<div class="MsoNormal">
<span lang="EN-US" style="font-family: Times, Times New Roman, serif;">Although not
very common, as any writes to a replicated cache are expensive, sometimes entry
processors are executed in replicated caches and unlike partitioned caches
where the entry processor is executed on the nodes that own the entries,in a replicated
cache the execution takes place on the member that invoked the entry processor.
It is important to note that the locking still takes place, however the locks
are implicit without extra the network calls, as would be needed if we were
using the methods in concurrent map interface.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="font-family: Times, Times New Roman, serif;"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="font-family: Times, Times New Roman, serif;">Cheers,</span></div>
Guilherme Melohttp://www.blogger.com/profile/04196746370820421715noreply@blogger.com0tag:blogger.com,1999:blog-839357368540751085.post-59334343189899424302012-06-23T11:20:00.000-07:002012-08-21T04:43:23.426-07:00Filtering Filters<b id="internal-source-marker_0.6319615943357348"><span style="font-family: Times, Times New Roman, serif;"><span style="background-color: transparent; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">Hello ! (Who needs a greeting anyway), distributed queries are a very powerful part of coherence, but can introduce show stopping performance degradation, so it is vital to get your filters right.</span><br /><span style="background-color: transparent; font-weight: normal;"><span style="background-color: transparent; vertical-align: baseline; white-space: pre-wrap;"></span></span><br /><span style="background-color: transparent; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">The filters provided by coherence, take an Extractor as a parameter, and will execute that extractor across all of the nodes, and the evaluate method will run on the result of that extractor.</span><br /><span style="background-color: transparent; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">This means that, if you are using a normal reflexion extractor(what is used by default when you call a filter with just a method name) new EqualsFilter(“getName”,”some_name”). This is quite slow and Inefficient, as every object will be deserialized in order to evaluate that filter. There are two ways to avoid this, </span><br /><span style="background-color: transparent; font-weight: normal;"><span style="background-color: transparent; vertical-align: baseline; white-space: pre-wrap;"></span></span><br /><span style="background-color: transparent; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">The simplest was to avoid this is to use an Index to store those values. indexed values are kept unserialized and are matched in the same was as regular db indexes. The Filter classes provided by coherence implement IndexAwareFilter, and know how to lookup those indexes, therefore avoiding the deserialization.</span><br /><span style="background-color: transparent; font-weight: normal;"><span style="background-color: transparent; vertical-align: baseline; white-space: pre-wrap;"></span></span><br /><span style="background-color: transparent; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">The other way is to simply pass a POF extractor as a parameter, POF extractors do not deserialize an object In order to extract the desired value. However this approach is slower than having Indexes, as each object will still be evaluated, as opposed to simply looking up the Index table. However, this provides more flexibility, as you can decide at run time which fields will be used.</span><br /><span style="background-color: transparent; font-weight: normal;"><span style="background-color: transparent; vertical-align: baseline; white-space: pre-wrap;"></span></span><br /><span style="background-color: transparent; vertical-align: baseline; white-space: pre-wrap;">Custom Filters:</span><br /><span style="background-color: transparent; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">Sometimes you need more complex logic than the custom filters provide you, so you may decide to code your own, the simplest way to do that is to simply implement the Filter interface, however, a filter created in this manner will not use indexes even if they are relevant and will deserialize every object in that particular cache resulting in catastrophic performance.</span><br /><span style="background-color: transparent; font-weight: normal;"><span style="background-color: transparent; vertical-align: baseline; white-space: pre-wrap;"></span></span><br /><span style="background-color: transparent; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">There are a few ways to create a custom filter efficiently:</span><br /><span style="background-color: transparent; font-weight: normal;"><span style="background-color: transparent; vertical-align: baseline; white-space: pre-wrap;"></span></span><br /><span style="background-color: transparent; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">Implementing EntryFilter instead of Filter allows you to implement the evaluateEntry Method, which receives a BinaryEntry as a parameter, allowing you to extract the values yourself in a clever manner, avoiding deserializing the whole entry, however the extraction will still occur for every entry, which can be time consuming.</span><br /><span style="background-color: transparent; font-weight: normal;"><span style="background-color: transparent; vertical-align: baseline; white-space: pre-wrap;"></span></span><br /><span style="background-color: transparent; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">Extending ExtractorFilter allows you to, through its superclasse’s constructor, to take advantange of the created indexes and, by implementing the evaluateExtracted method to write your complex custom logic to evaluate that Object.</span></span></b><br />
<b><span style="font-family: Times, Times New Roman, serif;"><span style="background-color: transparent; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span></span></b>
<b><span style="font-family: Times, Times New Roman, serif;"><span style="background-color: transparent; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">Cheers,</span></span></b>Guilherme Melohttp://www.blogger.com/profile/04196746370820421715noreply@blogger.com3London, UK51.5081289 -0.12800551.350006900000004 -0.443862 51.6662509 0.187852tag:blogger.com,1999:blog-839357368540751085.post-2406954616745571572012-05-12T09:40:00.000-07:002012-08-21T04:41:19.225-07:00Server Side Versioning<b id="internal-source-marker_0.555110418703407"><span style="font-family: Times, Times New Roman, serif;"><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">Hello World!(Nothing yet on the new greeting front), I was reading a very good post about versioned put on <a href="http://www.benstopford.com/2011/10/19/coherence-implementation-pattern-latestversioned-caches/" target="_blank">Ben Stopford blog's</a>, this stimulated me to run some tests and write something to perform server side versioning. </span><br /><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"> </span><br /><span style="vertical-align: baseline; white-space: pre-wrap;">Versioning</span></span></b><br />
<b><span style="font-family: Times, Times New Roman, serif;"><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">As Ben explained quite well on his blog many caches will need some concept of versioning, for instance, you might want to know not only the latest version of a piece of market data for a given instrument, but you might also want the last 30, to have a view of the overall movement of that instrument.</span><br /><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="vertical-align: baseline; white-space: pre-wrap;">Why Server Side Versioning</span></span></b><br />
<b><span style="font-family: Times, Times New Roman, serif;"><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">I wanted to write about server side versioning because that is the type of versioning i prefer to use, I find that Client side versioning makes distributed puts non trivial, can create bottlenecks and misses the opportunity to take advantage of Coherence’s distributed infrastructure.</span><br /><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="vertical-align: baseline; white-space: pre-wrap;">The Goals</span></span></b><br />
<ul style="margin-bottom: 0pt; margin-top: 0pt;">
<li style="list-style-type: disc; vertical-align: baseline;"><span id="internal-source-marker_0.555110418703407"><span style="font-family: Times, Times New Roman, serif; vertical-align: baseline; white-space: pre-wrap;">Multiple distributed clients must be able to push data at the same time and this should not affect the versioning.</span></span></li>
<li style="list-style-type: disc; vertical-align: baseline;"><span id="internal-source-marker_0.555110418703407"><span style="font-family: Times, Times New Roman, serif; vertical-align: baseline; white-space: pre-wrap;">Multiple Objects with the same business key must be kept in the cache in the same partition.</span></span></li>
<li style="list-style-type: disc; vertical-align: baseline;"><span id="internal-source-marker_0.555110418703407"><span style="font-family: Times, Times New Roman, serif; vertical-align: baseline; white-space: pre-wrap;">At any given time, a client, using the well known latest version marker (-1) must be able to access the latest version of an object with a simple get.</span></span></li>
</ul>
<b id="internal-source-marker_0.555110418703407"><span style="font-family: Times, Times New Roman, serif;"><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="vertical-align: baseline; white-space: pre-wrap;">The steps</span></span></b><br />
<ul style="margin-bottom: 0pt; margin-top: 0pt;"><b id="internal-source-marker_0.555110418703407"><span style="font-family: Times, Times New Roman, serif;">
<li style="font-weight: normal; list-style-type: disc; vertical-align: baseline;"><span style="vertical-align: baseline; white-space: pre-wrap;">The first step was to create a key class (in this example called versioned key) that wraps the business key and version, and that implements KeyAssociation, using the business key as the associated key. This will ensure that all of the objects for that same business key will share a partition.</span></li>
<li style="font-weight: normal; list-style-type: disc; vertical-align: baseline;"><span style="vertical-align: baseline; white-space: pre-wrap;">My Model class needed a version placeholder field, this will be explained in the the map trigger step, and is one of the main drawbacks of this approach.</span></li>
<li style="font-weight: normal; list-style-type: disc; vertical-align: baseline;"><span style="vertical-align: baseline; white-space: pre-wrap;">Creation of a map trigger that takes advantage of the Entries<link> getPreviousBinaryEntry() method to access the previous version of an entry, update its version and directly put it in the backing map.
</span></li>
</span></b></ul>
<span style="font-family: Times, Times New Roman, serif;"><b id="internal-source-marker_0.555110418703407"><span style="vertical-align: baseline; white-space: pre-wrap;">Workflow<span style="font-weight: normal;">
</span></span></b>
</span><br />
<ul style="margin-bottom: 0pt; margin-top: 0pt;"><b id="internal-source-marker_0.555110418703407"><span style="font-family: Times, Times New Roman, serif;">
<li style="font-weight: normal; list-style-type: disc; vertical-align: baseline;"><span style="vertical-align: baseline; white-space: pre-wrap;">Whenever a new VersionedKey Object is created its version is set to the well known latest version marker, in this example, -1.</span></li>
<li style="font-weight: normal; list-style-type: disc; vertical-align: baseline;"><span style="vertical-align: baseline; white-space: pre-wrap;">When an Entity is added for that key is added to the cache, the VersioningMapTrigger will be executed, having two different flows, one if it is the first object to be added, and another if there are already other versions of that Entity in the cache.</span></li>
<li style="font-weight: normal; list-style-type: disc; vertical-align: baseline;"><span style="vertical-align: baseline; white-space: pre-wrap;">If there are no other entries with that key in the cache (getPreviousBinaryEntry is null) all you need to do is to set the version in the Entity (not its key) to 1 using a POF updater to avoid deserializing it.</span></li>
<li style="font-weight: normal; list-style-type: disc; vertical-align: baseline;"><span style="vertical-align: baseline; white-space: pre-wrap;">If another entry exists in the cache for that same key (getPreviousBinaryEntry is not null), several step are required</span></li>
<ul style="margin-bottom: 0pt; margin-top: 0pt;">
<li style="font-weight: normal; list-style-type: circle; vertical-align: baseline;"><span style="vertical-align: baseline; white-space: pre-wrap;">The first step is to get the previous real version(not the version in the key, as it will be -1 since it was the latest) from the Entity that was already in the cache and set the version in the key of that Entity to the same value.</span></li>
<li style="font-weight: normal; list-style-type: circle; vertical-align: baseline;"><span style="vertical-align: baseline; white-space: pre-wrap;"> We then set the version on the Entity that was just added to that value +1, again using a POF Updater. </span></li>
<li style="font-weight: normal; list-style-type: circle; vertical-align: baseline;"><span style="vertical-align: baseline; white-space: pre-wrap;">Finally we put the previous entry and previous key directly in the backing map.
</span></li>
</ul>
</span></b></ul>
<span style="font-family: Times, Times New Roman, serif;"><b id="internal-source-marker_0.555110418703407"><span style="vertical-align: baseline; white-space: pre-wrap;">Code<span style="font-weight: normal;">
</span></span></b><script src="https://gist.github.com/2667300.js?file=VersioningTrigger.java">
</script></span><br />
<div>
<span style="font-family: Times, Times New Roman, serif;"><b id="internal-source-marker_0.555110418703407"><span style="vertical-align: baseline; white-space: pre-wrap;">Tests</span></b></span><br />
<script src="https://gist.github.com/2667470.js?file=testVersioning.java">
</script>
<b id="internal-source-marker_0.555110418703407"><span style="font-family: Times, Times New Roman, serif; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">This approach meets all of my requirements, and to be honest there is only one thing I do not like about it, and that is having the version on my model objects, however, since I cannot easily change the value of the key that will be added after a map trigger I decided it was worth it. I also tried some different approaches, such as my own implementation of a local cache and a backing map listener, however both of them required more backing map calls and were less performant, so I decided against it. </span></b></div>
Guilherme Melohttp://www.blogger.com/profile/04196746370820421715noreply@blogger.com0tag:blogger.com,1999:blog-839357368540751085.post-70878748659601387612012-04-11T17:07:00.000-07:002012-08-21T04:40:26.417-07:00Unit testing POF<span id="internal-source-marker_0.6471615491900593"><span style="font-family: Times, Times New Roman, serif;"><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">Hello world!(still looking for that better greeting...), It has become a recurrent topic here in the blog, ok, this is the first post and nothing is recurrent, however I hope it will be. </span><br /><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">POF is probably the most used type of serialization for coherence, because of its performance (it is around 10-12 times faster, which saves a lot on CPU cycles and the generated Binaries are about six times smaller than in normal serialization.) is far better than normal java serialization, however POF requires some configuration and implementation, as explained in the <a href="http://docs.oracle.com/cd/E24290_01/coh.371/e22837/api_pof.htm#BABEJCFF" target="_blank">documentation</a>. </span><br /><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="color: #222222; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">Another factor that need to be considered is the maintenance cost to the development process. It is important to make sure that mappings are up-to date to avoid, at best wasting time to fix something and at worst ending up with missing data or inverting two fields.</span><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="vertical-align: baseline; white-space: pre-wrap;"><b>Proposed solutions:</b></span><br /><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">The easiest and crudest way to do this is to simply have a unit test that puts all of your objects to their respective caches, then gets them and compares them to assert if they are equal. This however will take some time, as you must start a cache server, and, for these tests to be effective they must always be run at build time, and as you add more domain classes this will increase even more and your co-workers will no doubt plot to murder you.</span><br /><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">When you are testing POF there is something simpler you can do, something I picked up from Dr. Alberto Forti on a project we worked together a while ago.</span><br /><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">You can easily create and use the configurable POF context directly. The ability to do that also opens up some interesting possibilities in using POF with external systems, a topic I will cover on a later post. </span><br /><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">Below is an example of a unit test using Configurable POF context:</span></span><span style="font-family: Arial; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"></span><br />
<script src="https://gist.github.com/2667292.js?file=testSomeEntitySerialization.java"></script>
<span style="font-family: 'Courier New', Courier, monospace;"><span style="font-size: 15px; white-space: pre;"><br /></span></span></span>Guilherme Melohttp://www.blogger.com/profile/04196746370820421715noreply@blogger.com7Unknown location.50.12057809796007 0.4614257812548.837823097960069 -2.06542971875 51.403333097960072 2.98828128125