tag:blogger.com,1999:blog-48044593764504948962024-02-18T23:29:17.627-08:00Random ParallelsNotes from using tech in the real world - dev, ops, architecture, data science, teams ... and some random stuffjmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.comBlogger37125tag:blogger.com,1999:blog-4804459376450494896.post-47688111298749762082021-09-26T08:11:00.000-07:002021-09-26T08:11:30.795-07:00Couple of tools for Duplicate Photos and Media Titles<p>
Despite saving nearly all of my photos stored online straight from my phone, I
still many photos and other media stored locally. In part, this is due to
having photos from other cameras now and over the years or other media (music
library for example).
</p>
<p>
However, curation is a bit of a headache so I only try to clean every once in
a while. Here are a few of the commands that I use to remove duplicates, fix
titles or make other changes.
</p>
<h4 style="text-align: left;">Music and Movie Titles</h4>
<p>
To check titles on media files like movies or music, I print out the filename
(easy to find via ls!) and the title using exiftool like this (the line break
between File Name and Title is important to grep -F):<br />
</p>
<p>
<span style="font-size: x-small;"><span style="font-family: courier;">ls -1 | while read a; do echo "$a"; exiftool "$a" | grep -F "File
Name<br />Title" ; echo " "; done</span></span>
</p>
<p>If I want to update the title: <br /></p>
<p>
<span style="font-size: x-small;"><span style="font-family: courier;">exiftool -Title=Zombie_Island my_homemade_zombie_movie.m4v</span></span>
</p>
<p></p>
<h4 style="text-align: left;">Duplicate Photos</h4>
<p>
To find duplicate photos, you could rely on the filename, but you might find
that the same filename (image_001.jpg) was used for a number of images. So,
the best thing to do is compare checksums on the files:
</p>
<p>
<span style="font-size: x-small;"><span style="font-family: courier;">find . -type f -exec md5sum {} \; | sort | uniq --all-repeated=separate
-w 15 > dupes.txt</span></span><br />
</p>
<p>
and fine the duplicates to keep:<br /><span style="font-size: x-small;"><span style="font-family: courier;">grep -A1 ^$ dupes.txt | grep / > dup-keep.txt<br />wc -l dup-keep.txt
#to check how many</span></span>
</p>
<p>
find the files to move to a holding directory prior to deleting rather than
deleting straight away - something could go wrong!:<br /><span style="font-size: x-small;"><span style="font-family: courier;">grep -vf dup-keep.txt dupes.txt > dup-move.txt # slow if there are
many...<br />grep -f dup-keep.txt dup-move.txt # should be
empty</span></span><br />
</p>
<p>
remove the empty lines and check:<br /><span style="font-size: x-small;"><span style="font-family: courier;">grep -v ^$ dup-move.txt > dup-m1.txt<br />grep -c / dup-move.txt
dup-m1.txt <br />grep -c / dup-m1.txt <br />mv dup-m1.txt
dup-move.txt</span></span>
</p>
<p>
test out "moving" the duplicates into the duplicates directory. I say "moving"
as it's really a copy and delete:
</p>
<p>
<span style="font-size: x-small;"><span style="font-family: courier;">mkdir duplicates</span></span>
</p>
<p>
<span style="font-size: x-small;"><span style="font-family: courier;">cut -d" " -f3-99 dup-move.txt | head | cpio -pvd duplicates<br />cut -d"
" -f3-99 dup-move.txt | head | while read a; do echo $a; \rm "$a" ;
done</span></span></p><p><span style="font-size: x-small;"><span style="font-family: courier;"> </span></span><br />If that looks good, then do the rest:<br /><span style="font-size: x-small;"><span style="font-family: courier;">cut -d" " -f3-99 dup-move.txt | cpio -pvd duplicates<br />cut -d" "
-f3-99 dup-move.txt | while read a; do echo $a; \rm "$a" ; done</span></span><br />
</p>
<p>Check for duplicates in the local directory again:</p>
<p>
<span style="font-size: x-small;"><span style="font-family: courier;">mv duplicates ../ <br />
find . -type f -exec md5sum {} \; | sort | uniq --all-repeated=separate
-w 15 > dupes.txt</span></span><br /><br />
</p>
<p><br /></p>
jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-26811631723876260312021-08-09T00:38:00.001-07:002022-08-09T00:47:28.819-07:00ActiveMQ recover corrupt KahaDB<p> Getting a message about corrupt files or missing indexes from kahadb when ActiveMQ starts?</p><p>We've seen that sometimes copying the kahadb entry to another broker and trying again (there are differences in the Java and sometimes ActiveMQ versions) works.</p><p>Another way that will work is to make a backup of the kahadb directory and then in the original location, delete <b>db.redo</b> and <b>db.data</b> files. This will cause ActiveMQ to rescan the indexes and rebuild the information. It will take a little time; I'd estimate 10-20s for a GB, but that will depend heavily on your infrastructure. For 50GB, it took roughly 10 min for us.</p><p><span style="font-size: x-small;"><span style="color: #f3f3f3;"><span style="background-color: #444444;"><span style="font-family: courier;">-rwxr-xr-x. 1 activemq activemq 8 Jul 22 21:31 lock<br />-rw-rw-r--. 1 activemq activemq 1725984 Jul 22 21:36 db-35.log<br />-rw-rw-r--. 1 activemq activemq 6156701 Jul 22 21:42 db-36.log<br />-rw-rw-r--. 1 activemq activemq 14464113 Jul 22 21:48 db-37.log<br />-rw-rw-r--. 1 activemq activemq 3695664 Jul 22 21:53 db-38.log<br />-rw-rw-r--. 1 activemq activemq 9236794 Jul 22 21:59 db-39.log<br />-rw-rw-r--. 1 activemq activemq 3233605 Jul 23 19:53 db-34.log<br />-rwxr-xr-x. 1 activemq activemq 3299608 Jul 24 13:09 db.redo<br />-rwxr-xr-x. 1 activemq activemq 45191168 Jul 24 13:09 db.data</span></span></span></span><br /></p><p>Delete the bottom two: db.redo and db.data then restart activemq and watch the logs for progress in recovering the messages.<br /></p>jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-27175960080880754482020-08-08T10:00:00.001-07:002021-01-03T14:54:16.935-08:00Trying out GraalVM<div>Interested in running Java a bit like running C/C++ or Go? I wanted to give it a try: <a href="https://www.graalvm.org/">GraalVM</a> is a very interesting project for a few reasons - a new Java JIT, running a variety of languages on the JVM, and, most importantly for here, the native image capability. Using native images means programs for the JVM are compiled down to binaries just like C, C++, Go and other languages increasing performance, decreasing the size of the binary, and removing the need to install the JVM on the target container or computer. <br /></div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9faosEAjc_hZS3xoYHIZAwvnQcDMhy8s_BekmOL9Zxlpd1gzP4REhTlGvU-tvpPiQrsJhRWZ5pYTc7o5DE3S7CLl7ZPiCveUxt-M3HzjTZ_vqx4Jz8HQ5q7zUSTw_wXxcdzuh2L-toN8/s2048/american-public-power-association-XGAZzyLzn18-unsplash.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1363" data-original-width="2048" height="266" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9faosEAjc_hZS3xoYHIZAwvnQcDMhy8s_BekmOL9Zxlpd1gzP4REhTlGvU-tvpPiQrsJhRWZ5pYTc7o5DE3S7CLl7ZPiCveUxt-M3HzjTZ_vqx4Jz8HQ5q7zUSTw_wXxcdzuh2L-toN8/w400-h266/american-public-power-association-XGAZzyLzn18-unsplash.jpg" width="400" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><font size="2"><span>Photo by <a href="https://unsplash.com/@publicpowerorg?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">American Public Power Association</a> on <a href="https://unsplash.com/s/photos/energy?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></span></font>
<br /></td></tr></tbody></table><div><br /></div><div>Most important to me was the performance and efficiency - the promise of
Java code that could run faster and/or more efficiently (Java already
is efficient according to <a href="https://sites.google.com/view/energy-efficiency-languages/home">this study</a> but still some ways to go to reach C level performance), but the other features are also appealing: a small, deployable binary, would Python code on GraalVM run faster than JPython did, memory savings, faster startup times, and so on. </div><div><br /></div><div>The take away is that many of these are delivered, but not all at once or all equally. Performance of native images sometimes lagged the normal JVM significantly.</div><div><br /></div><div>To get started, download the GraalVM. I used Java 11 rather than Java 8 although I later found that Java 11 has poorer performance vs Java 8 or the newest (at the moment) Java 14 (OpenJDK 14). Once downloaded and unpacked (tar xzvf ...), cd into the directory that the unpacked download was put into. Then set some environment variables:</div><div style="margin-left: 40px; text-align: left;"><b><span style="color: #f3f3f3;"><span style="font-family: "courier";"><span style="background-color: black;">export JAVA_HOME=$PWD</span></span></span></b></div><div style="margin-left: 40px; text-align: left;"><b><span style="color: #f3f3f3;"><span style="font-family: "courier";"><span style="background-color: black;">export PATH=$PWD/bin:$PATH</span></span></span></b></div><div style="margin-left: 40px; text-align: left;"><b><span style="color: #f3f3f3;"><span style="font-family: "courier";"><span style="background-color: black;">which java; java -version<br /></span></span></span></b></div><div style="margin-left: 40px; text-align: left;"><b><span style="color: #f3f3f3;"><span style="font-family: "courier";"><span style="background-color: black;">which gu</span></span></span></b></div><div>The last few commands are to check that you're accessing the right Java location and version and that the gu "GraalVM Component Updater" is available. The <b>java -version</b> produced output including this: OpenJDK Runtime Environment GraalVM CE 20.1.0.</div><div><br /></div><div>Using this trivial Java code, let's give GraalVM a try - here's SimpleGraalExample.java: <br /></div><div style="margin-left: 40px; text-align: left;"><span style="color: #6fa8dc;">public class SimpleGraalExample {
<br /> public static void main(String[] args) {
<br /> System.out.println(System.currentTimeMillis());
<br /> System.out.println("Hello from the simplest GraalVM Example");
<br /> }
<br />}
</span><br /></div><div><br /></div><div>Compile that with normal Java and run it:</div><div style="margin-left: 40px; text-align: left;"><b><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";">date +%s%N; java SimpleGraalExample <br />1595759258474307392 <br />1595759258719 <br />Hello from the simplest GraalVM Example <br /><br /></span></span></span></b></div><div>The date call returns the time since the epoch in seconds and then nanoseconds while the Java code returns the time in milliseconds. This gives us a rough way to see how long the jvm took to start up and run the first statement - 719ms - 474ms = 245ms in this case (there are things happening on the command line that will account for some of the time. I think there's a way to get the JVM to report the startup time so will update.)</div><div><br /></div><div>To make a native image, you'll need to install a number of libraries and gcc: gcc, glibc-devel, zlib-devel, and libstdc++, then install the Graal native image component using <b>gu</b>:</div><div style="margin-left: 40px; text-align: left;"><b><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";">gu install native-image</span></span></span></b><br /></div><div><br /></div><div>Next run:</div><div style="margin-left: 40px; text-align: left;"><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";"><b>native-image SimpleGraalExample</b></span></span></span></div><div>On my system, it took about 10s and used about 2GB of memory to produce the native image. Once done, you should have an executable file named <b>simplegraalexample</b> (all lower case - Linux/Unix standard). Run that:</div><div style="margin-left: 40px; text-align: left;"><b><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";">date +%s%N; ./simplegraalexample <br />1595759279619196068 <br />1595759279628 <br />Hello from the simplest GraalVM Example</span></span></span></b></div><div>The startup time has dropped to roughly 9ms. So, a single file executable with a fast startup time - definitely useful for containers that are set to autoscale under load especially if this shrinks start up times for larger applications that would normally take several seconds to load. <br /></div><div><br /></div><div>In this example, we've not really stretched the JIT aspect at all. Java has two tiers to the current JIT compiler - the C1 "quick and decent improvements" tier and the C2 "deeper optimizations that take longer" tier. Here's a description from the <a href="http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/hotspot/share/runtime/simpleThresholdPolicy.hpp">C++ JDK code</a>:</div><div><br /></div><span style="font-family: "courier";"> * The system supports 5 execution levels:
<br /> * * level 0 - interpreter
<br /> * * level 1 - C1 with full optimization (no profiling)
<br /> * * level 2 - C1 with invocation and backedge counters
<br /> * * level 3 - C1 with full profiling (level 2 + MDO)
<br /> * * level 4 - C2
</span><br /><div><br /></div><div>The idea behind the new JIT in Graal is to write it in Java so that it's easier to maintain and extend and one that avoids any memory issues of the current C++ versions.</div><div><br /></div><div>To test the JIT and Graal's performance more, use the <a href="https://www.graalvm.org/docs/examples/java-performance-examples/">CountUpperCase example</a> on the GraalVM site. I've added a call to System.currentTimeMillis() at the start.</div><div style="margin-left: 40px; text-align: left;"><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";"><b>javac CountUppercase.java<br /></b></span></span></span></div><div style="margin-left: 40px; text-align: left;"><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";"><b>date +%s%N; java CountUppercase What kind of Performance would anyOne expect from Graal and the new JIT compiler
<br />1595760201725131521
<br />1595760202020
<br />1 (687 ms)
<br />2 (199 ms)
<br />3 (159 ms)
<br />4 (158 ms)
<br />5 (140 ms)
<br />6 (142 ms)
<br />7 (169 ms)
<br />8 (151 ms)
<br />9 (141 ms)
<br />total: 69999993 (2096 ms)</b></span></span></span><br /></div><div>The time for the first vs second and subsequent iterations shows the effects of compilation. If you want to see the compilation happen, add <span style="font-family: "courier";"><b>-Dgraal.PrintCompilation=true</b></span> to the java execution above.</div><div><br /></div><div>Run this again turning off the new JIT compiler and run with the default:</div><div style="margin-left: 40px; text-align: left;"><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: "courier";">date +%s%N; java -XX:-UseJVMCICompiler CountUppercase What kind of Performance would anyOne expect from Graal and the new JIT compiler
<br />1595760345136700939
<br />1595760345238
<br />1 (1051 ms)
<br />2 (957 ms)
<br />3 (954 ms)
<br />4 (940 ms)
<br />5 (943 ms)
<br />6 (950 ms)
<br />7 (942 ms)
<br />8 (953 ms)
<br />9 (958 ms)
<br />total: 69999993 (9596 ms)</span></b></span></span><br /></div><div><br /></div><div>Below, I tried to "turn on" tiered compilation (C1 + C2) another way, but since this is the default behavior since Java 8, I'm not sure what it really did as the results look much less like "UseJVMCICompiler" and much more like the Graal JIT.<br /></div><div><br /></div><div style="margin-left: 40px; text-align: left;"><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: "courier";">date +%s%N; java -XX:+TieredCompilation CountUppercase What kind of Performance would anyOne expect from Graal and the new JIT compiler
<br />1595760835130792310
<br />1595760835241
<br />1 (602 ms)
<br />2 (279 ms)
<br />3 (194 ms)
<br />4 (235 ms)
<br />5 (165 ms)
<br />6 (220 ms)
<br />7 (154 ms)
<br />8 (271 ms)
<br />9 (168 ms)
<br />total: 69999993 (2451 ms)
</span></b></span></span><br /></div><div><br /></div><div>To stop at level 2 JIT, use a command like this: <br /></div><div style="margin-left: 40px; text-align: left;"><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";"><b>java -XX:-UseJVMCICompiler -XX:TieredStopAtLevel=2 CountUpper...</b></span></span></span><br /></div><div><br /></div><div>To see the native image performance:</div><div style="margin-left: 40px; text-align: left;"><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";"><b>native-image CountUppercase</b></span></span></span></div><div><div style="margin-left: 40px; text-align: left;"><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";"><b>date +%s%N; ./countuppercase What kind of Performance would anyOne expect from Graal and the new JIT compiler
</b></span></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";"><b>1595780282446553637
</b></span></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";"><b>1595780282455
</b></span></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";"><b>1 (1467 ms)
</b></span></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";"><b>2 (1227 ms)
</b></span></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";"><b>3 (1222 ms)
</b></span></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";"><b>4 (1211 ms)
</b></span></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";"><b>5 (1219 ms)
</b></span></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";"><b>6 (1216 ms)
</b></span></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";"><b>7 (1233 ms)
</b></span></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";"><b>8 (1252 ms)
</b></span></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";"><b>9 (1277 ms)
</b></span></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-family: "courier";"><b>total: 69999993 (12585 ms)
</b></span></span></span><br /></div>An almost instant startup, but we've lost performance here - seemingly lots.<br /></div><div><br /></div><div>The native-image tool has a code profiling optimization that is only available in the enterprise version and a tracing agent that helps identify which classes will be used. The latter can be helpful for reducing time by instantiating classes at compile time. To use the tracing agent, run the code normally and exercise the code paths. For the simple CountUpperCase.java, it's run it like this:</div><div> </div><div><b><span style="font-family: courier;"><span style="background-color: black;"><span style="color: #f3f3f3;">java -agentlib:native-image-agent=config-output-dir=./META-INF/native-image/ CountUppercase What kind of Performance would anyOne expect from Graal and the new JIT compiler</span></span></span></b></div><div> </div><div>This will put four files into META-INF/native-images. In this case, the files were close to empty, so I knew this wouldn't make much of a difference. However, in a larger application, the trace will help identify class usage better than static analysis.</div><div>Now, run:</div><div style="margin-left: 40px; text-align: left;"><b><span style="font-family: "courier";"><span style="color: #f3f3f3;"><span style="background-color: black;">native-image -cp ./META-INF/ CountUppercase</span></span></span></b></div><div style="margin-left: 40px; text-align: left;"><b><span style="font-family: "courier";"><span style="color: #f3f3f3;"><span style="background-color: black;">date +%s%N; ./countuppercase What kind of Performance would anyOne expect from Graal and the new JIT compiler</span></span></span></b></div><div style="margin-left: 40px; text-align: left;"><b><span style="font-family: "courier";"><span style="color: #f3f3f3;"><span style="background-color: black;">1595782668222550375
<br />1595782668277
<br />1 (1364 ms)
<br />2 (1177 ms)
<br />3 (1168 ms)
<br />4 (1174 ms)
<br />5 (1154 ms)
<br />6 (1163 ms)
<br />7 (1152 ms)
<br />8 (1165 ms)
<br />9 (1209 ms)
<br />total: 69999993 (12008 ms)</span></span></span></b></div><div>Unfortunately, not much of a gain at all. <br /></div><div><br /></div><div>While this runs slower, tests with a simple web app (using HTTPServer) show very little difference in performance - faster or slower suggesting that for APIs and Spring boot apps, native image would have little impact. (Will add code...)<br /></div><div><br /></div><div>Testing the Java scimark program, here are the steps and results (javac and javac -O produced similar results as you'd expect if the JIT rather than javac was doing most of the optimization):</div><div style="margin-left: 40px; text-align: left;"><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: "courier";">javac jnt/scimark2/commandline.java</span></b></span></span></div><div style="margin-left: 40px; text-align: left;"><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: "courier";">time java -classpath . jnt.scimark2.commandline
<br /> <br />SciMark 2.0a
<br /> <br />Composite Score: 1050.056478100177
<br />FFT (1024): 1036.3878030994783
<br />SOR (100x100): 857.5678798364443
<br />Monte Carlo : 408.1116711801417
<br />Sparse matmult (N=1000, nz=5000): 1338.152124290685
<br />LU (100x100): 1610.0629120941362
<br /> <br />java.vendor: GraalVM Community
<br />java.version: 11.0.7
<br />os.arch: amd64
<br />os.name: Linux
<br />os.version: 5.7.8-100.fc31.x86_64
<br /> <br />real 0m31.560s
<br />user 0m31.959s
<br />sys 0m0.098s
</span></b></span></span><br /></div><div><br /></div><div>Using the -UseJVMCICompiler option to turn off the new compiler:<br /></div><br /><div style="margin-left: 40px; text-align: left;"><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: "courier";">time java -XX:-UseJVMCICompiler -classpath . jnt.scimark2.commandline
<br /> <br />SciMark 2.0a
<br /> <br />Composite Score: 1442.2801308185528
<br />FFT (1024): 914.6516464184309
<br />SOR (100x100): 1068.4530497649632
<br />Monte Carlo : 664.6498065711132
<br />Sparse matmult (N=1000, nz=5000): 1135.3139812371085
<br />LU (100x100): 3428.3321701011487
</span></b></span></span><br /></div><div><br /></div><div>Again trying with the +TieredCompiler option was more in line with the GraalVM JIT results:</div><div><div style="margin-left: 40px; text-align: left;"><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: "courier";">time java -XX:+TieredCompilation -classpath . jnt.scimark2.commandline
<br /> <br />SciMark 2.0a
<br /> <br />Composite Score: 1053.134652575841
<br /></span></b></span></span></div><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: "courier";">.....</span></b></span></span></div><div>I suspect that it's not turning off the new JIT. <br /></div><div><br /></div><div>Creating a native image and running it. First, add a manifest file, create a jar and then run native-image:</div><div style="margin-left: 40px; text-align: left;"><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;">cat META-INF/MANIFEST.MF
<br />Main-Class: jnt.scimark2.commandline<br /></span></b></span></span></div><div style="margin-left: 40px; text-align: left;"><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;">jar cmvf META-INF/MANIFEST.MF scimark2.jar jnt/scimark2/*.clas<br /></span></b></span></span></div><div style="margin-left: 40px; text-align: left;"><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;">java -jar scimark2.jar</span></b></span></span></div><div style="margin-left: 40px; text-align: left;"><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;">native-image -jar scimark2.jar commandline # wait about 40s to run<br /></span></b></span></span></div><div style="margin-left: 40px; text-align: left;"><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;">./commandline</span></b></span></span></div><div style="margin-left: 40px; text-align: left;"><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;"><br /></span></b></span></span></div><div><div style="margin-left: 40px; text-align: left;"><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;">SciMark 2.0a
</span></b></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;"> </span></b></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;">Composite Score: 665.0761252118398
</span></b></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;">FFT (1024): 678.1650940478376
</span></b></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;">SOR (100x100): 877.8838572232665
</span></b></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;">Monte Carlo : 31.565788470303204
</span></b></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;">Sparse matmult (N=1000, nz=5000): 695.5266550424169
</span></b></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;">LU (100x100): 1042.2392312753743
</span></b></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;"> </span></b></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;">java.vendor: Oracle Corporation
</span></b></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;">java.version: 11.0.7
</span></b></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;">os.arch: amd64
</span></b></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;">os.name: Linux
</span></b></span></span><br /><span style="background-color: black;"><span style="color: #f3f3f3;"><b><span style="font-family: courier;">os.version: 5.7.8-100.fc31.x86_64
</span></b></span></span><br /></div><br /></div><div>Again, the performance here has dropped (also note the change in java vendor). While investigating this, I came across <a href="https://github.com/oracle/graal/issues/979">this comment from adinn</a> which I could summarize as: "why would you expect the static compiler to produce code as fast as the JIT?" He carries on with an excellent explanation. However, the reason I'd expect it to run as fast or faster than JIT is that C/C++ code with the static compiler based optimizers run very fast (faster than Java). Undoubtedly, there is more performance available, but overall, the ability to make native images (no JVM installation in the production system), fast start ups, run other programming languages, and the potential for the new (Java based) compiler make GraalVM a very interesting project.<br /></div><div><br /></div><div>I haven't added stats for running GraalVM against a project that combines with large frameworks like Spring. I'll add that later. However, this is another place that Graal shines - by evaluating code paths and throwing away unnecessary code, the binaries are much smaller and start up much faster (as above) than normal.<br /></div><div><br /></div>jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-81211146321500695272020-05-27T13:34:00.001-07:002020-05-27T13:34:41.876-07:00Quaternions<div style="text-align: left;"><h2>What are Quaternions and why would anyone care? </h2><div><div class="_3bJ2H CHExY"><div class="_1l8RX _1ByhS"><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIypVzG4sQ6heS8UfI_VGnCe7BJmFtEM5bWChklJjeaQ74BUBma59VoaG3276Af6jDegcWT_Kr_OQpYVOcwyyHDMjjxwUiZQ7avzNgqYXazj85bKW10wemvL2MLVmkZZ2j29OgFif3tl8/" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="2560" data-original-width="1920" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIypVzG4sQ6heS8UfI_VGnCe7BJmFtEM5bWChklJjeaQ74BUBma59VoaG3276Af6jDegcWT_Kr_OQpYVOcwyyHDMjjxwUiZQ7avzNgqYXazj85bKW10wemvL2MLVmkZZ2j29OgFif3tl8/w300-h400/karlis-reimanis-Y31Z6Mf7rys-unsplash%25282%2529.jpg" width="300" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><span>Photo by <a href="https://unsplash.com/@reims?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Karlis Reimanis</a> on <a href="https://unsplash.com/s/photos/physics?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></span></td></tr></tbody></table><span><br /></span></div><div class="_1l8RX _1ByhS"><br /></div></div></div></div>
<div>Quaternions are the next step up in complex numbers. Complex numbers are real numbers with an 'imaginary' part. 'imaginary' is in quotes as that's what we call it, but it's not fiction, it's the square root of -1, often represented by i (or j in electrical engineering). Written differently i<sup>2</sup>=-1. In early days, people considered this strange and useless so imaginary is fitting. By the way, if you go back far enough people didn't see any point in 0 either.</div><div><br /></div><div>A real number is represented by a - any real number like 2, 1.5781739, 10000, etc. A complex number is represented by a + bi where a and b are real numbers and i<sup>2</sup>=-1. Complex numbers are very good at represented rotations (as in a circle around 0) which is why they're important in many scientific and engineering activities.</div><div>
<br />
</div><div>Quaternions are the next step up: a + bi + cj + dk or a<sub>0</sub> + ia<sub>1</sub> + ja<sub>2</sub> + ka<sub>3</sub>. Here i<sup>2</sup> = j<sup>2</sup> = k<sup>2</sup> = -1. All good so far, but it gets more complex in that ij = k = -ji, jk = i = -kj, and ki = j = -ik. That leads to ijk = -1 which can be seen from ij=k and k*k = -1. The fact that ij = k and not -1 is a little confusing, but these are more than just the square root of -1, but directions in 'quaternion space'. What these look like is the outcome of a standard vector cross product of i x j = k and j x i = -k. <br /></div><div><br /></div><div>What's interesting about quaternions is that they have almost all the properties of real and complex numbers in terms of addition, subtraction, multiplication, division, and inverses, except that multiplication is not commutattive. In other words, for normal numbers a*b = b*a, but as we saw for quaternions ij = -ji.</div><div><br /></div><div>In terms of their use, quaternions are useful for rotations in higher dimensions that planes where complex numbers are good. In many ways the 4 dimensional approach resembles relativity theory where time plus the three spatial coordinates are linked or common differential equations where time and space are linked - especially in Schrodinger's equation where time and space are linked via a multiplier of i. For fun purposes, quaternions can replace complex numbers to create higher dimensional Mandelbrot sets. Don't stop there - there are also Tessarines another 4-dimensional complex number were j<sup>2</sup> = 1 and Octonions - an 8th dimensional complex number.<br /></div><div><br /></div><div>The story is that Hamilton was trying to solve a difficult problem and came up with the idea of triplets of complex numbers while out on a walk with his wife by the Royal Canal in Dublin. Supposedly, he was so struck by the idea that he carved i<sup>2</sup> = j<sup>2</sup> = k<sup>2</sup> = -1 and ijk = -1 into the stone of a bridge.<br /></div>jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-28442849041335026392020-02-16T14:16:00.002-08:002022-10-09T04:19:18.781-07:00Other Useful Linux Commands<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgb0hsZuIkCaZ6iWrzX6W0guuBA80G2kUyqfNCPrnatC6aIzMHhjgt5k1WvUjJ6iYppHucmK7UkjlOFp3UhIa2PVsoc7wE6PDvVYGAqMDDxqive1dTaEEhyphenhyphen0dfnVxCuLFwFUPzMtL0Mcp8/s1600/Screenshot+from+2020-02-16+20-07-18.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="369" data-original-width="626" height="235" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgb0hsZuIkCaZ6iWrzX6W0guuBA80G2kUyqfNCPrnatC6aIzMHhjgt5k1WvUjJ6iYppHucmK7UkjlOFp3UhIa2PVsoc7wE6PDvVYGAqMDDxqive1dTaEEhyphenhyphen0dfnVxCuLFwFUPzMtL0Mcp8/s400/Screenshot+from+2020-02-16+20-07-18.png" width="400" /></a></div>
<br />
<h3>
A collection of <i>other</i> bash and Linux commands ... or solutions</h3>
There's obviously no point in detailing all of the Linux/Unix/Bash commands available. Here are a few commands or solutions, I wanted to remember.<br />
<br />
<u>Get motherboard or DIMM info:</u><br />
<b>dmidecode</b> reads information from the DMI (desktop management interface) table which is closely related to the SMBIOS (system management BIOS). Need sudo to run:<br />
<br />
<code> dmidecode -t 4 # for CPU info</code><br />
<code>dmidecode -t 2 # for motherboard info </code>
<br />
<code>dmidecode -t memory # for all memory </code> <br />
<code>dmidecode -t 17 # for sodimm information </code>
<br />
<code>dmidecode -t 16 # for motherboard info on memory </code><br />
<br />
There's also <b>lshw</b>, but it wasn't installed on my system.<br />
Related commands: lspci, lsusb, lscpu, lsscsi, lsblk<b><br /></b><br />
(Other ls* commands of interest: lsmem, lslocks, lsns, lsipc, lslogins)<b> </b><br />
<br />
<u>Weather on the command line:</u><br />
<code>curl wttr.in</code><br />
<code>curl wttr.in/Denver</code><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiger21zGLHiBw4D6YZr9inM-7VBJ9ldhZz8d-c0ipPGiBJxIB1dtopNvUpuzxmAenj8AxxtOK03X7H_E4GfHwN6pAUmVuEc3KJc_H1jbbQo5XKbJPzLxWQl1313rw6hr-lFJU9Sd0Dmxw/s1600/Screenshot+from+2020-02-16+20-04-34.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="345" data-original-width="1016" height="135" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiger21zGLHiBw4D6YZr9inM-7VBJ9ldhZz8d-c0ipPGiBJxIB1dtopNvUpuzxmAenj8AxxtOK03X7H_E4GfHwN6pAUmVuEc3KJc_H1jbbQo5XKbJPzLxWQl1313rw6hr-lFJU9Sd0Dmxw/s400/Screenshot+from+2020-02-16+20-04-34.png" width="400" /></a></div>
<br />
<u>While loop failing:</u><br />
I had a problem where I wanted to check ssh on a number of hosts. Simple - cat the file, pipe into while, use ssh with timeout to do the check like this:<br />
<code>cat host_list.txt | while read h; do timeout 3 ssh $h; done</code><br />
<br />
It didn't work. It just hung on the ssh command until timeout cut the command. ssh seemed to be <a href="https://unix.stackexchange.com/questions/66154/ssh-causes-while-loop-to-stop" target="_blank">grabbing standard input</a> (and maybe stdout) and interfering with the while loops input. Switching to a for loop helped with the input. Then ssh didn't seem to be the best choice nor did telnet 22. Netcat in a for loop worked best:<br />
<code> nc -v $host 22 </code><br />
<br />
Problem solved and the hosts with ssh running were found....<br />
<br />
<u>ssh starting remote processes: </u><br />
Here's another one that might be easy or not... With ssh, you can run a remote command:<br />
<code>ssh user@remote.host "ls -l"</code><br />
What if you want to start a remote process and want it to keep running - something like this:<br />
<code>ssh user@remote.host "nohup java example.jar &"</code><br />
<br />
The problem with the above is that it often won't work - the remote service won't be running. This mostly has to do with terminal control. Despite nohup saying that it is redirecting output to nohup.out, there's still a problem. Two easy solutions:<br />
<code>ssh user@remote.host "nohup java example.jar > ./output_file.out 2>&1 &"</code><br />
<br />
This grabs both standard out and standard error and puts them into output_file.out. Of course, another option is to create a systemd (or init.d) script and use that. <br />
<br />
<u>inotify-tools:</u><br />
Specifically, <b><span style="font-family: "courier new" , "courier" , monospace;">inotifywait</span></b> which will watch file system objects for events that you specify.<br />
<code> inotifywait -r -e access,modify /var/log </code><br />
This will watch for access or modification updates on files in /var/log.<br />
Combine inotifywait with a while loop as in<br />
<code> while inotifywait -e access /var/log<br /> do<br />
some shell commands here<br />
done </code><br />
<br />
The only downside to inotifywait is that inotify-tools often needs to be installed (yum/dnf install or apt install). <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqfaamVnxz6geWCpII56PnYmBp1Z8JWQ1qxVdOy7GSBPvzij_5qqcdLrw5yTg2c5hS92UqaS9EmUot7e8_z-x2fno-d5ShfejI-ucYaZ2sSPBXTCVp_PC_NUpsSKgiITldEeN19lKkNu0/s1600/Screenshot+from+2020-03-15+09-47-05.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="81" data-original-width="550" height="58" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqfaamVnxz6geWCpII56PnYmBp1Z8JWQ1qxVdOy7GSBPvzij_5qqcdLrw5yTg2c5hS92UqaS9EmUot7e8_z-x2fno-d5ShfejI-ucYaZ2sSPBXTCVp_PC_NUpsSKgiITldEeN19lKkNu0/s400/Screenshot+from+2020-03-15+09-47-05.png" width="400" /></a></div><p>
<br /><u>History Time and Date</u></p><p>To have more info about when commands were run, set HISTTIMEFORMAT <br /></p><p><span style="font-size: x-small;"><span style="font-family: courier;">HISTTIMEFORMAT="%d/%m/%y %T "</span></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: courier; margin-left: 1em; margin-right: 1em;"><img alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAcMAAAApCAYAAABJLkKOAAAABHNCSVQICAgIfAhkiAAAFANJREFUeF7tnQW0HTUTx9NS3N29uDsc3L24leJycJdSpEDRQrFCS3F31yLF3SmnheLQU0pxd+jHb/hyT96+3SQrt+++d2fOuefeu0kmk/9mM5NJNtNpjjnmGGuUUhE45JBDzN9//2369++fml72Yr35l5VPy7d/BJZaailz4YUXmg022MD8+OOPhRqk/bQQbFqonSHQpZ3JO07FPe+88+paX73511V4Zd7QCIw//vjmqaeeMr/99pvp2bNnYUXY0I1U4RSBChHopDPDCtFUVopAB0Sga9euZuzYseaDDz7ogK3TJikC/yGgylB7giKgCCgCikDTI9C56RFQABQBRUARUASaHgFVhk3fBRQARUARUAQUAVWGOfrACiusYDbccMMcJfJlrTf/fNJobkVAEciDwKabbmpuvvnmzCL1fr7rzT+zYR0kwasMp5hiCsO26nvuucc8/PDDrZrcuXNnc8QRR5hnnnlGdq4dfPDBplOnTrV8pB933HHm2WefNc8//7w555xzzMQTT1xLX2ihhczVV19tXn31VfP000+b7t27t6qjzIWQfHnr33///c0PP/xQE6ksPsm2JfmH5AvVn+Sf/O8rP/XUU5vhw4e3+IwYMcJ8+OGHZrLJJkuySv2/0UYbSX6Xz0EHHVTLG0pPZepcDOHjlu/Tp4956623QixbpCP3PPPMU7tGfS6PK664Qv6///775r333pPfF198cS1/ly5dTK9evcxLL71k3njjDXPllVeaBRdcUNJ32WUXyZ8sz/8ddtghmA4PX/2TTDKJ+eSTTwx9ytKhhx4q1yaYYAK5dOCBB4rsVg6+eQ4tLbvssubjjz9ukU6eWWaZRbLY9r3wwgvyfB911FFmvPHGq5UfOHCg3Hue72HDhplbbrnFLLbYYpIeI59ldN9995nbb7+9xpcfIfxaZM74s+qqqwoeCyywgFliiSXk93LLLSe5Z5hhBlFst956q9lqq61qHOgDfIqQ+3y3R/5um0Py1/v+FME/VMb7aoXdQUanX2+99Vrx2nXXXc3iiy9u1l13XYPiueyyy8yXX35pbrjhBsm79dZbmyWXXFLS4dW3b1+z9957m/PPP1/yH3744WbQoEHmxRdfNLwPdc0115i3337bvP76663qKnLBJ1/e+meeeWYZGJ977rmaKGXxcduU5B8jX6j+EGa+8t9++61ZeOGFW7BgYP/555/NTz/9FGIt6Sjbu+++2zAIp1EoPa2MvRaDj8272mqrmXnnndfHrlDa7rvvLuVOPvlk8/vvv5tTTz21BR/6OnWj3L744guzxhprmFVWWcW88847YgTy8ZUPpYfq5x5uvPHG5qKLLpJ60t41vOuuu8SgzaLPP//crLjiiqnJJ5xwgiiSLbfcUoxg3sc9+uijzWmnnVbLf/bZZ4vSnnTSSc12220nxsLKK68s6THy/bvb3fzxxx9SfqaZZjLIA8Xglyq0c5HXTiDunSX7e+eddxZlSP994oknRBljBPbr10/amJeSz3d7459sb0j+et+fpDxV/PfODHlJ99577xWrL4123HFHQ2f/9ddfZZCko3DN0rTTTitW8S+//CJ5HnroIenQ0D///GP23HNPUS682P7KK6+YJ598UpRnVeSTL2/9WId33HGHKHVLZfFx25nkHyNfqP4QjnnKY6xgNfMCd5IYNOzA7aah7L766qtk9tr/ULrNmMY/Bh/KTznllOakk04yZ511VqYc9UpAUTAj5JUEizUG47iiv/76y/DBiGM28/XXX7eYuZWRY5pppjHbb7+9vMP42WefmVGjRomSYJBM8xwwPtx2221mttlmE8UGxcjHssSjjz5qhgwZYtZff/0yIrcqi4EC8W1/jxkzRq5NN910ZuTIkTI2ITuzaZT8mWeeWfMO0A761WuvvWYGDx5sll9++VZ12AvJ5zvEn1k5z9Tll18u4y+zWLxzjzzyiPTpJOXlT59AbuslgB8GDePxXHPNlWTf6n9I/lYFClzw3Z8C7IJFvMrQVxrg5pxzTvPuu++KUuMhwOKde+65a8Vwr+JH32mnnWSWsM0228gDkUXww21TBcXIl6zHVz+dzSd7klfe+mP4++RL1l/1/yOPPFJm9AwOLuE1YNBfZJFFZLbvEg8t7icUAAptv/32M7wMbimUTj4f/xaV/fsnDR/co8xMRo8encxe9/8YT5tttpksFVgjsO6VOhXgisSYZXbYrVs3c//994t7sgqaf/75Rbl+9NFHNXa4ivEapM3Cude77babGAYoFyhGPqsMUYi41fPQtddeKwrE/bhGG7PM77//Xox1ZqnIZY23N998U3BbdNFFzZ9/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" /> </span></div><div class="separator" style="clear: both; text-align: center;"><span style="font-family: courier; margin-left: 1em; margin-right: 1em;"> </span></div><div class="separator" style="clear: both; text-align: center;"><span style="font-family: courier; margin-left: 1em; margin-right: 1em;"> </span></div><div class="separator" style="clear: both; text-align: left;"><u>JQ json query</u><br /></div><div class="separator" style="clear: both; text-align: left;">Query json files using jq. More complex example where top level json is a list of sets of fields with lists themselves which might be optional and require a select to handle:</div><div class="separator" style="clear: both; text-align: left;"> </div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: courier; margin-left: 1em; margin-right: 1em;">jq '.[] | "\(.name), \(.projectKey), \(.team.name), \(.team.id), " + ((select(".source[].orgName") | .source[].orgName)//null)' pretty-c1.json <br /></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: courier; margin-left: 1em; margin-right: 1em;">jq . json-in-one-line.json > pretty-c1.json<br /></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: courier; margin-left: 1em; margin-right: 1em;"><br /></span></div>pretty-c1.json is a json file made pretty with the second command above<br /><p><span style="font-size: x-small;"></span></p>jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-85277811647411517692019-05-27T04:05:00.001-07:002019-06-04T22:44:50.816-07:00Quantum Computing<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvO-hJ0ar4VWwbqq4vMGT3RqwoeGKMMCjN1VUe-rNtsg-G1gPTd8bCTJaYX7Zw5fKBdB2YQUDcNKDVQ3hH3NqCFmdi_9EN1uLfdlVta9dhOM39gJL-PGbUqgrJcy1gNL2rlgbgq7YzCu0/s1600/Screenshot+2019-05-27+at+11.36.31.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="723" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvO-hJ0ar4VWwbqq4vMGT3RqwoeGKMMCjN1VUe-rNtsg-G1gPTd8bCTJaYX7Zw5fKBdB2YQUDcNKDVQ3hH3NqCFmdi_9EN1uLfdlVta9dhOM39gJL-PGbUqgrJcy1gNL2rlgbgq7YzCu0/s400/Screenshot+2019-05-27+at+11.36.31.jpg" width="400" /></a></div>
<div style="text-align: center;">
<span style="font-size: x-small;">Spinning coin - curvature is due to <a href="https://en.wikipedia.org/wiki/Rolling_shutter">CMOS rolling shutter</a> </span></div>
One of the biggest conceptual and architectural changes to computing is quantum computing - using the nature of quantum mechanics to change the way calculations are done. Modern computers already use quantum mechanics in some fashion due to semiconductors. However, that use is mainly to create logic gates that mirror the gates created with vacuum tubes and relays. In a quantum computer, the logic directly relies on important features from quantum mechanical probabilities: superposition and entanglement of states. Quantum computers are believed to be able to quickly solve problems that the fastest modern computers struggle to solve - the Quantum Supremacy (of quantum computers over classical computers for a type of problem). Theoretical work shows that widely used asymmetric key cryptography would be easily broken meaning secure data like credit card or password information would be insecure. There are limits as quantum computers won't solve problems that normal computers cannot solve now. Richard Feynman, the noted physicist, first mentioned quantum computers in 1959 and in the 80s, Paul Benioff and Yuri Mann developed the ideas for these computers. <br />
<h4>
Some Background </h4>
<b>Probability</b> is the likelihood of something happening. Flip a coin and the probability of it landing heads up is 1/2. That's the same probability for landing tails up. Added together, the probability for heads or tails is 1 (assuming that the coin won't land and remain stationary on its edge). We think of probabilities as values between 0 and 1. 0 is no chance of an event occurring while a value of 1 indicates it will occur. Usually, the value is somewhere in between. With quantum mechanics, this is more complicated - probability in these systems comes from squaring the probability amplitude. That amplitude can be positive or negative and combining amplitudes that are positive or negative will create regions of stronger or weaker amplitude (it's more interesting as the amplitudes are complex numbers). Square those modified amplitudes to get probabilities. This is how atoms bond together in molecules, liquids or solids - the electrons (quantum mechanical wave-particles) of an atom can have positive and negative amplitudes that combined with electrons of other atoms create bonding and anti-bonding combinations. In mathematical terms, classical probability is like:<br />
<div style="text-align: center;">
<b>Sum(p_i) = 1</b></div>
where each p_i >= 0.<br />
In quantum mechanical terms, probability is like this:<br />
<div style="text-align: center;">
<b>Sum(|a_i|^2) = 1</b></div>
where each a_i is a complex number and can be negative<br />
<br />
<b>Superposition</b> is an important concept in understanding quantum mechanical systems. It's the concept behind Schrodinger's Cat if you've heard of that. A quantum mechanical system can be composed of multiple states all at the same time. It's like a spinning coin - heads, tails, heads, tails. Only when you measure the system/state or check the coin do you get a definite answer. Measurement causes a collapse of the superposition of states into one state. Before measuring, the 'spinning' or unchecked state of the coin could be seen like this:<br />
<div style="text-align: center;">
<b>|Total State> = (|head> + |tail>)/sqrt(2)</b></div>
The probability of that state is the square (eliminate the head-tail situation):<br />
<div style="text-align: center;">
<b>1/2Heads + 1/2 Tails = 1</b> (as the total probability needs to be 1). </div>
While the measured state would be, if the coin is heads up:<br />
<div style="text-align: center;">
<b>|Total State> = |head></b></div>
with probability being 1 and Heads = 1. <br />
<br />
<b>Entanglement</b> is when two quantum systems combine so that again there is a way of describing the total system as a sum of the two. Imagine that we had two coins and each must show the opposite of the other; if one is heads, the other has to be tails. These two coins would be entangled - while the coin analogy is simplistic, this does happen with electrons and something called spin. Two electrons can be in the same 'state' along as one is spin up and the other is spin down. (I put 'state' in quotes as the total state of each electron actually includes the spin.) If you measure one of the coins, if you check if it's heads or tails, then you instantly know what the other coin is. Einstein referred to this as a 'spooky action at a distance' (see a <a href="https://en.wikipedia.org/wiki/EPR_paradox">description of EPR</a>). Recent experiments have shown that it's true for particles separated by hundreds of miles but are still entangled. Entropy (information theory entropy) can also be used as a measure of entanglement (the more entropy then more entanglement). To say this another way: for entangled think correlated in probability and that outcomes are related. If two probabilities are
uncorrelated, then the outcome is simply the combined probability (P_coin1(heads) times P_coin2(tails)). Correlated
outcomes aren't that simple<b> </b>and can't be decomposed like that. Entanglement makes it an inseparable whole. If heads is 1 and tails is 0, then the two entangled states would be: <b>|01>+|10></b>.<br />
<br />
If this is a little confusing, Feynman's comments: "safely say that nobody understands quantum mechanics." Which probably isn't true as Feynman understood it, but it's not easy to understand. The important part is that quantum mechanics allows parts of a system to interact in such a way that 'calculations' by the system can use the entangled probabilities to reach a solution much faster than a classical computer.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhY6bNhOGBvEp6iNf-TKUdqC42MWUq-4-NxNmyZa9l69dwTClSmBnbKQ6B3yz9-dFm3C1tmEjqCm9skKp8-mxffpM952SAWfZz15pSFEQYS-zQAb7y9xs-CP394c7oIQgHd-nsZjA5IYgg/s1600/Screenshot+2019-05-27+at+11.58.57.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="694" data-original-width="1600" height="172" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhY6bNhOGBvEp6iNf-TKUdqC42MWUq-4-NxNmyZa9l69dwTClSmBnbKQ6B3yz9-dFm3C1tmEjqCm9skKp8-mxffpM952SAWfZz15pSFEQYS-zQAb7y9xs-CP394c7oIQgHd-nsZjA5IYgg/s400/Screenshot+2019-05-27+at+11.58.57.jpg" width="400" /></a></div>
<div class="_3bJ2H CHExY" style="text-align: center;">
<div class="_1l8RX _1ByhS">
<span style="font-size: xx-small;">Photo by <a href="https://unsplash.com/photos/2mEx6GSAl0c?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Mathew Schwartz</a> on <a href="https://unsplash.com/search/photos/computer-boards?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></span></div>
</div>
<h4>
Quantum Computing</h4>
Normal computers have bits, 0s and 1s, in registers or memory and use these to perform calculations with normal digital logic gates. These 0s and 1s are held in memory with electricity and have to be bolstered frequently to keep them at 0 or 1. In a quantum computer, the qubit is the basic bit. As above, the qubit doesn't hold a 0 or 1, but holds a superposition of 0 and 1 - it holds those amplitudes mentioned above. Having a larger number of qubits means that they can be entangled. Entangling them means the group of qubits can hold all possible states at once. Two qubits can hold the states 00, 01, 10, 11 all at once. A classical computer would have to hold these as 4 separate states, requiring more bits. Ultimately, the number of possible solutions that a quantum computer can keep at once scales as 2^n where n is the number of qubits. A normal computer would need 2^n memory for the same number of states. A quantum computer with two qubits allows a superposition of 4 states, three qubits allows 8 states, ..., n qubits allow 2^n states. Qubits hold the state of the interaction while it reduces the memory required.<br />
<br />
The result of a quantum calculation with n qubits would only return an n 'bit' answer even though the calculation ran over 2^n bit space. Again, with n bits in a classical computer, n amount of information can be encoded; a quantum computer will span 2^n space, but only return n bits of information.<br />
This rapid increase in possible states and the ability to cover all of the states is what leads to the potential speed up in quantum computers.<br />
<br />
Calculations are done by 'loading' the information into the qubits, influencing their state, much like setting the state of memory in a normal computer even if the method is very different. Depending on the type of quantum computer, this loading is done with microwaves or lasers. Operations are reversible and operate on the quantum mechanical analog of a normal n-bit
register; classical computer gates are not reversible -
you can't know the input from the output. (In normal computers, the NOT gate is reversible; also normal computers could be built with <a href="https://en.wikipedia.org/wiki/Toffoli_gate">Toffoli gates</a> which can be reversed using extra memory.) One of the most well-known quantum computing gates is the <a href="https://en.wikipedia.org/wiki/Quantum_logic_gate">Hadamard gate</a> which transforms specific states into a superposition of states and back again. As an example, in a classical computer running a probability calculation (random/stochastic/probability/transition
matrices), the goal is to preserve that the sum of the probabilities = 1. In a quantum computer, operations like the Hadamard gate (unitary
matrices) preserve that sum of squares of the amplitude = 1. Positive and negative amplitudes combine to increase or decrease the probabilities of some outcomes while keeping the overall probability of an outcome correct. The entanglement of the qubits means that affecting one affects all - in the two coins example above, setting one coin to heads meant the other was tails. Another analogy is pulling a red ball out of a bag of red and green balls - a normal computer would pull one, but quantum entanglement means that pulling one is like pulling two or more out.<br />
<br />
Again, the quantum computer qubits hold all values until measured; a classical computer would hold one value in the same number of bits. Measurements are then done (again with microwaves or lasers) to 'collapse' the 2^n states into one that is the result with n bits of info (the original number of qubits). Measurement is not reversible.<br />
<br />
<b>Issues with Quantum Computers</b><br />
Due to the nature of quantum computing, the outcomes are probabilistic - only provide a valid solution with some probability. This is true in normal computers, too, but the effort over decades of work has been to reduce the error to near 0 so that we trust the outcome. Almost no one thinks about error correction when using a normal computer these days. Error correction in quantum computing is in its infancy. The computers have to be kept super cold and isolated as much as possible as any noise could affect the calculation. Realistically, calculations would need to be run several times to make certain of the result. The errors aren't just value errors as in a classical computer, but errors in the way the qubits interact. While the quantum computer is built to have its qubits interact with each other as much as possible (entanglement), it's also built to keep it from interacting with the environment as little as possible! The noise can cause decoherence which means the qubits stop interacting as one group. Environmental issues like heat, noise, electromagnetic or other radiation all work against the system. The redundancy of calculations for error corrections have been estimated to be as high as hundreds, thousands or even millions of times; fixing this is a key area of research. A recent advance has reduced the number of qubits needed for cracking 2048 PKI and overcome errors from <a href="https://www.technologyreview.com/s/613596/how-a-quantum-computer-could-break-2048-bit-rsa-encryption-in-8-hours/">billions to millions</a>. Scaling the lifetime of the qubits before decoherence - <a href="https://www.technologyreview.com/s/609451/ibm-raises-the-bar-with-a-50-qubit-quantum-computer/">IBM in 2017 achieved 90 microseconds for 50 qubits</a>.<br />
<br />
Scaling quantum computers is another challenge not just to eliminate environmental factors. They need to be scaled in coherence time and number of qubits as well as programming techniques. A small number of qubits easily fit close to each other, but when the number of qubits grows very large, distances from each other become an issue. In terms of programming, the same challenge facing classical computers exists - simple hardware with complex languages or complex hardware with simple languages (or the worst of both). The difficulty with quantum computers is the complexity of understanding the operations. A computer with n qubits might require n^3 logic 'gates' or operations to achieve the results. <br />
<br />
<h4>
<b>Current Work</b></h4>
There are two main types of quantum computers now: ion/atom traps and superconducting circuits. Ion traps use lasers to write, read and trap the ions suspended in the electromagnetic fields. The ions in the computer interact in a way that you could think as vibrating together. Similarly, atoms can be trapped in an optical lattice.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLRBNuCaauVSIgzwui1bQxSeWs5CqewUkVwTo0BZa3x7a5p0GCXfPw7MZypoQU-s7nIaaaK56gn1bPhUz_jSG95blgHVnmtKyR-6TMG2b-OBgSDaQ4_AweOiAubJpUWT_WqOSLJ_JcMpg/s1600/Screenshot+2019-05-27+at+12.02.17.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="818" data-original-width="1600" height="203" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLRBNuCaauVSIgzwui1bQxSeWs5CqewUkVwTo0BZa3x7a5p0GCXfPw7MZypoQU-s7nIaaaK56gn1bPhUz_jSG95blgHVnmtKyR-6TMG2b-OBgSDaQ4_AweOiAubJpUWT_WqOSLJ_JcMpg/s400/Screenshot+2019-05-27+at+12.02.17.jpg" width="400" /> </a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-size: xx-small;">Photo by<a href="https://en.wikipedia.org/wiki/Quantum_computing#/media/File:DWave_128chip.jpg"> D-Wave Systems, Inc from WikiCommons</a></span> </div>
<br />
The other main type is superconductor based. These are often modeled on superconduction quantum interference devices (SQUIDs) small rings of current. Oscillating microwaves to set and read values. The devices are connected together via radio frequency channels which could be part of a printed circuit board. A company called <a href="https://www.dwavesys.com/home">D-Wave</a> have been working on quantum computers for years. They structure the computer to solve the problem so requires almost no code; their computers don't connect all qubits together and can't solve all types of problems. Their computers resembling annealing systems using transverse tunneling fields (analogous to temperature in real annealing). Google and IBM are working on more generic computers that link all qubits. In 2017, IBM has produced systems with up to 50 qubits. In 2018, Google has raised that to 72 qubits. Compared to classical computers doing simulated annealing or quantum Monte Carlo calculations, <a href="https://ai.googleblog.com/2015/12/when-can-quantum-annealing-win.html">Google says quantum annealing is 100M times faster</a>. IBM offers a <a href="https://quantumexperience.ng.bluemix.net/qx/community">test quantum computing environment</a> if you want to sign up. <br />
<br />
Quantum supremacy is when a quantum computer is able to solve a classical computer science problem faster than a supercomputer (or any classical computer). It's uncertain how large a quantum computer will have to be to achieve this, but it's expected soon by some, others doubt it will happen. To be clear, quantum computers will solve the same types of problems that classical computers can, but they'll solve them much faster. They're both Turing machines. Those problems a classical computer can't solve like the halting problem or NP-complete problems will be unsolvable by a quantum computer, too. In acronyms, the quantum computer is BQP bounded error, quantum, polynomial time set thought to be bigger than BPP bounded error, probabilistic polynomial time set. In particular, quantum computers should be good at factoring large numbers, calculating optimized paths (like courier routes), quantum search (Grover's algorithm), and physics and chemistry problems around the quantum nature of the universe as nature isn't 'classical' it's quantum mechanical.<br />
<br />
With the ability to factor large numbers quickly, quantum computers will crack traditional cryptography easily. In 1994, <a href="https://en.wikipedia.org/wiki/Shor%27s_algorithm">Shor developed an algorithm</a> that can quickly factor large numbers or discrete logarithms. This algorithm based on modular exponentiation and quantum Fourier transforms can find the factors of public key cryptography (PKI for public key infrastructure) and render PKI insecure. In 2016, NIST requested submissions of <a href="https://www.nist.gov/news-events/news/2019/01/nist-reveals-26-algorithms-advancing-post-quantum-crypto-semifinals">quantum-resistant cryptography algorithms</a>. Currently, 26 algorithms are in the later stages of evaluation. The key weakness of PKI is the asymmetric key portion which relies on large prime numbers or discrete logarithms being difficult for classical computers to factor. Symmetric keys are much more resistant to quantum computing, but Grover's algorithm do make them susceptible - thankfully, doubling the symmetric key size is often enough in a post-quantum world.<br />
<br />
Some approaches to post-quantum cryptography include one time keys (hashes or OTP), simultaneous/multivariate equations, lattice-based and error correcting codes. All rely on the difficulty of decoding the information without all the input or the time it takes to decode. Current PKI also relies on the hardness of solving problems. It's possible a classical computer can crack a PKI key as well, but the algorithms have been designed to make it take thousands of years. <br />
<br />
<style type="text/css"><!--td {border: 1px solid #ccc;}br {mso-data-placement:same-cell;}--></style>The drive to improve quantum computers is growing as being the first with a viable quantum computer is important financially and strategically. Microsoft has entered the race with Google, IBM, D-Wave and researchers around the world. There's a <a href="https://brilliant.org/courses/quantum-computing/?utm_campaign=msft_qc_blog">course to learn to program for quantum computers</a> from Microsoft and Google, IBM has a <a href="https://quantumexperience.ng.bluemix.net/qx/community">system online</a> for learning more, Google has it's own <a href="https://opensource.google.com/projects/quantum-computing-playground">playground and scripting language</a>, Microsoft has announced a <a href="https://docs.microsoft.com/en-us/quantum/language/?view=qsharp-preview">language Q#</a>, and researchers at labs are driving this forward. <br />
<br />jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-82522634459663423082019-04-28T22:55:00.001-07:002019-04-28T22:55:41.173-07:00Sizes of Large Directories with Gluster, SSHFS, NFSThis post covers a few things: checking the number of entries or rough sizes of a directory, a look at the behavior of Gluster, NFS, SSHFS, and SFTP for remote directory sizes, and some info on mounting remote file systems using NFS or SSHFS (Gluster is a topic for another day).<br />
<br />
First, how to check the rough size of a directory. We know that checking the number of files in a large directory locally can be slow. Doing that check over a remote mount like
Gluster can be much slower and even cause the Gluster mount to crash.<br />
Generally, under Linux/Unix, you can get a rough estimate of the size of a directory by looking at the output of<span style="font-family: "Courier New", Courier, monospace;"> <b>ls -l</b></span> in the parent directory of that directory. For
example, let's use a test directory to check behavior: ~/test. We've added two subdirectories dir_small with no files in it and dir_large with 5000 files in it. ls -l ~/test gives:<br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">ls -l ~/test </span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">drwxrwxr-x. 2 jm jm 143360 Apr 26 16:41 dir_large<br />drwxrwxr-x. 2 jm jm 4096 Apr 26 16:56 dir_small</span></span></span></span><br />
Here
dir_small has the smallest size possible and dir_large is larger due to
the 5000 files in it. Remember that in Linux/Unix, a directory is just a special type
of file that keeps information about the list of files in that
directory. <br />
<br />
In
order to count the files or just a guide as to which is the largest directory, the safest options are:<br />
<span style="font-size: x-small;"><span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;">ls -l ../ # only a rough guide, check parent directory to see directory 'size'</span></span></span></span><br />
<span style="font-size: x-small;"><span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;">find dir_large -type f -print | wc -l # lists files one by one</span></span></span></span><br />
<span style="font-size: x-small;"><span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;">echo * | awk -F ' ' '{print NF}' # provides the list of files in one line</span></span></span></span><br />
<span style="font-size: x-small;"><span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;">ls | wc -l # performs more ops than echo * (as checked by strace)</span></span></span></span><br />
If you think you have a large directory, don't do <span style="font-family: "Courier New", Courier, monospace;"><b>ls -l </b></span>as that will stat each file requiring far more ops and time.<br />
<br />
I point all of this out as this was tried using Gluster with some issues. In case you don't know, <a href="https://www.gluster.org/">Gluster or GlusterFS</a> is a clustered file system. We've used it for stand alone clusters within our data centers (DC). It can do cross DC replication although we've found too many problems so don't use that feature.<br />
<br />
We had an issue the other day with a system mounting a Gluster volume and we wanted to check on files in a large collection of directories. <br />
<br />
Interestingly, Gluster's directory sizes don't show the usual file size info. For the same directories as above, it showed<br />
<span style="color: white;"><span style="background-color: black;">drwxrwxr-x. 2 jm jm 4096 Apr 26 16:41 dir_large<br />drwxrwxr-x. 2 jm jm 4096 Apr 26 16:56 dir_small</span></span><br />
<br />
Running other commands like ls and find can be painful with a large Gluster volume. Some have given it a <a href="http://moo.nac.uci.edu/~hjm/fhgfs_vs_gluster.html#_summary">reputation for being almost unusable</a> for certain interactive operations like ls, find, du, etc. Ultimately, the solution was to use find and it's one file at a time checking; the alternative is to run this commands on the Gluster server rather than the client system.<br /><br />
Since Gluster has a different meaning for directory size, we thought we'd see whether that is Gluster or the <a href="https://en.wikipedia.org/wiki/Filesystem_in_Userspace">FUSE</a> system that it uses. Interesting, the Gluster brick, the file system being exported for remote mounting, shows the normal directory size. That size would be expected as it's a normal Linux file system. We also tried NFS which passes through the usual directory size:<br />
<span style="background-color: black;"><span style="color: white;">drwxrwxr-x. 2 jm jm 143360 Apr 26 16:41 dir_large<br />drwxrwxr-x. 2 jm jm 4096 Apr 26 16:56 dir_small</span></span><br />
NFS doesn't use FUSE though so this only confirms that normal directory sizes are passed through with other remote file systems. <br />
<br />
We also tried <a href="https://github.com/libfuse/sshfs">SSHFS</a> which uses FUSE for mounting. Just like NFS, SSHFS showed the right directory sizes:<br />
<span style="background-color: black;"><span style="color: white;">drwxrwxr-x. 2 jm jm 143360 Apr 26 16:41 dir_large<br />drwxrwxr-x. 2 jm jm 4096 Apr 26 16:56 dir_small</span></span><br />
By the way, SFTP also showed the normal directory sizes, but you might assume that by now.<br />
<br />
So, this looks like Gluster is the issue here and has chosen to re-interpret the meaning of a directory size.<br />
<br />
<b>Information on mounting NFS and SSHFS file systems</b><br />
Instructions for SSHFS:<br />
SSHFS isn't installed on all systems. On my test system (Fedora 29), I needed to install with<br />
<span style="background-color: black;"><span style="color: white;">sudo dnf install fuse-sshfs</span></span><br />
<span style="background-color: black;"><span style="color: white;"># make a mount point </span></span><br />
<span style="background-color: black;"><span style="color: white;">mkdir ~/test/mount_point</span></span><br />
<span style="background-color: black;"><span style="color: white;"># to mount, do: </span></span><br />
<span style="background-color: black;"><span style="color: white;">sshfs remote_user@remote_host:/remote_directory ~/test/mount_point</span></span><br />
<span style="background-color: black;"><span style="color: white;"># to unmount, do:</span></span><br />
<span style="background-color: black;"><span style="color: white;"> fusermount -u ~/test/mount_point</span></span><br />
<br />
Instructions for NFS:<br />
<span style="background-color: black;"><span style="color: white;">#edit the export file and add your export directory</span></span><br />
<span style="background-color: black;"><span style="color: white;">vi /etc/exports</span></span><br />
<span style="background-color: black;"><span style="color: white;"># add /home/jm/test localhost(ro) *.local.domain(ro)</span></span><br />
<span style="background-color: black;"><span style="color: white;">#start nfs-server</span></span><br />
<span style="background-color: black;"><span style="color: white;">sudo systemctl start nfs-server</span></span><br />
<span style="background-color: black;"><span style="color: white;">sudo exportfs -a</span></span><br />
<span style="background-color: black;"><span style="color: white;"># check the exported file system is available from the local or client system:</span></span><br />
<span style="background-color: black;"><span style="color: white;">showmount -e test-nfs-server.local.domain</span></span><br />
<span style="background-color: black;"><span style="color: white;">showmount -e localhost</span></span><br />
<span style="background-color: black;"><span style="color: white;"># make a mount point</span></span><br />
<span style="background-color: black;"><span style="color: white;">mkdir /tmp/mount_point</span></span><br />
<span style="background-color: black;"><span style="color: white;">sudo mount -t nfs test-nfs-server.local.domain:/home/jm/test /tmp/mount_point</span></span><br />
<span style="background-color: black;"><span style="color: white;"> #to unmount, use the usual umount:</span></span><br />
<span style="background-color: black;"><span style="color: white;">sudo umount /tmp/mount_point</span></span><br />
<span style="background-color: black;"><span style="color: white;"># on the nfs server, un-export the fs:</span></span><br />
<span style="background-color: black;"><span style="color: white;">sudo exportfs -ua</span></span><br />
<span style="background-color: black;"><span style="color: white;">#stop nfs if you want:</span></span><br />
<span style="background-color: black;"><span style="color: white;">systemctl stop nfs-server</span></span><br />
<span style="background-color: black;"><span style="color: white;"> </span></span>jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-3832171731918179222019-04-22T01:37:00.001-07:002019-05-06T14:41:56.730-07:00MySQL Galera Split BrainThe Galera cluster option for MySQL is one advantage MySQL has over Postgres. The clustering allows high availability and good performance. However, it's not without its issues and one or two of those is the split-brain problem. There can be two different kinds of split brain: poorly configured clusters that can't achieve a quorum for the split you're worried about and, more rarely, an update issue with the nodes which is interesting if frustrating.<br />
<br />
MySQL Galera clusters have worked well and provided good uptime. The normal configuration is to have more active nodes in the primary location or data center. In the event of the link between the primary and secondary sites failing, the primary cluster should continue to run. It will continue running provided it has the majority of quorum votes on its own. It is important, therefore, to make sure that the quorum is achievable without the secondary location. One option is to keep the number of active nodes higher in the primary location. Another is to adjust the <i><b>pc.weight</b></i> of each node to make sure that the weight is larger in the primary location. See the <a href="http://galeracluster.com/documentation-webpages/weightedquorum.html">Galera docs about setting the weight</a> of a node. Either of these options makes the primary location safe from failures of the other locations, but still presents a problem if the primary data center has an issue. The remaining option is to use a 3rd location or witness to break ties or provide a quorum - you could do that with your own software, with a full set of servers in a 3rd location or use Galera's own solution. Galera's solution is garbd, <a href="http://galeracluster.com/documentation-webpages/arbitrator.html">the Galera Arbitrator</a>, which acts as a witness or voting system when you only really have two main locations.<br />
<br />
The <b>second</b> split brain issue is more interesting - i.e. it isn't a simple configuration or quorum issue. In this one, a Galera cluster shuts down on its own after detecting an issue. All of the active nodes except one would report something like this:<br />
<br />
"<span style="font-size: small;"><span style="font-family: "courier new" , "courier" , monospace;">Duplicate entry 'entry_value_being_inserted' for key 'Key_for_column', Error_code: 1062</span></span> "<br />
It might include "<span style="font-family: "courier new" , "courier" , monospace;">handler error HA_ERR_FOUND_DUPP_KEY</span>" as well.<br />
<br />
The issue here is that the Galera replication has pushed updates to every node. The replication pushes the change synchronously, but applies asynchronously - flow control is used to prevent nodes from getting too far behind - <a href="http://galeracluster.com/documentation-webpages/managingfc.html">see the Galera docs (and first sentence)</a> ('commits asynchronously' from these <a href="http://galeracluster.com/documentation-webpages/nodestates.html">Galera doc</a>s). As you'd expect, it's RBR - row-based replication, not statement-based. What happens here is a case of a node falling behind. Each of the other nodes see an inconsistency and ,in order to protect the cluster, shut themselves down. Unfortunately, this could be every node shuts down except the one lagging, inconsistent node. With only one node active, Galera will realize it dosen't have the majority of votes to maintain the cluster and shuts the remaining node down. In order to recover the cluster, you need to find the last node that was running and start it with the <span style="font-family: "courier new" , "courier" , monospace;">bootstrap</span> option. Then start every other node as normal. This issue doesn't happen often based, but it's good to understand it and how to recover it when it does happen. By the way, there are some related issues that could do the same so see this link for how to recover: <a href="https://bobcares.com/blog/error-no-1062/">this for fixing this and related issues.</a>jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-46923008629424690662019-04-20T02:02:00.003-07:002021-10-02T10:15:54.451-07:00ActiveMQ Network of Brokers Again (Multi-DC)<b>Network of Brokers with ActiveMQ</b><br />
In the past, I've written about the ActiveMQ network of brokers and some of the issues that can come up (see for example: https://www.randomparallels.com/2012/07/network-of-brokers-revisited.html).<br />
<br />
That was some years ago, so what is it like now? Overall, it's very good and reliably works between different data centers. It also provides for an important use case: the ability to write to one location and read anywhere. This is write-read is done via the 'local' broker to the apps so that if both the producer and consumer are in the same location, it's done 'locally' without having to commit to all brokers (or a quorum) everywhere first. That fact can have important performance benefits. However, there does seem to be a limit to the number of queues that can be bridged across a network of brokers - I don't know the number or what affects it, but assume a few hundred at most. The good news is that you can have a number of network connectors between two brokers and that means you can have much more than 100s of queues bridged between the same pair of brokers. The key is to create new networkConnectors as needed to handle groups of related (or similarly named) queues or topics. Here's an example of different groups of queues per networkConnector within the networkConnectors part of the ActiveMQ configuration xml:<br />
<br />
Here's an example of the network connector config:<br />
<pre class="highlight"><span style="color: white;"><span style="background-color: black;"><code><networkConnectors>
<networkConnector name="QUEUE_SET_1" duplex="true" uri="static:(tcp://192.x.x.x:61616)">
<dynamicallyIncludedDestinations>
<queue physicalName="stock.nasdaq.>" />
</dynamicallyIncludedDestinations>
</networkConnector>
<networkConnector name="QUEUE_SET_2" duplex="true" uri="static:(tcp://192.x.x.x:61616)">
<dynamicallyIncludedDestinations>
<queue physicalName="stock.nyse.>"/>
</dynamicallyIncludedDestinations>
</networkConnector>
</networkConnectors></code></span></span></pre>
<br />
<br />
Splitting out groups of queues like this allows the brokers to bridge more queues (or topics, the same applies for them) between the brokers without any issues. For another example, look at the Duplex Connector example on the <a href="http://activemq.apache.org/networks-of-brokers.html">ActiveMQ docs</a> or in <a href="https://dzone.com/articles/active-mq-network-brokers">this article</a>.jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-77863059529940357292019-03-03T11:11:00.000-08:002019-03-03T13:31:20.363-08:00Java vs C Benchmarks<br />
(Written a few years ago, but moving to my main blog - original date March 2014)<br />
Benchmarks are much like statistics - so easy to
mislead people about what they mean. They are useful to be able to
compare systems, programs, etc, and it is not uncommon when a new system
is available, that everyone wants to try out their app to see how fast
it will be. This was common at universities where the latest big system
needed to be tested with every individual's idea of the 'most important'
programs. Of course, the benchmark is really a factor of a number of
changes - hardware, compiler, configuration, libraries, etc.<br />
<br />
A
common benchmark these days is between computer languages. It used to be
that C and FORTRAN were always considered the fastest languages with
C++ close behind. With the increase in the number of languages and
investment in making ones like Java much faster than the first iteration
(Java 1.1 was about as fast as bash for running a program, but now it
is close to C in many benchmarks), comparisons between languages are
extra fuel on the fire of programming language wars/discussions.<br />
<br />
There is a great site for such comparisons: <a href="https://benchmarksgame-team.pages.debian.net/benchmarksgame/which-programs-are-fast.html">Computer language benchmark games (previously shootout)</a><br />
<br />
This
shows that C, C++, and now Rust instead of FORTRAN still generally hold
the favored positions although not for every micro-benchmark. Sometimes
Java or another language will win out. The benchmark games also include
source code and compile commands if you want to repeat it yourself.<br />
<br />
Another fun tool is <a href="http://math.nist.gov/scimark2/">SciMark2</a> a Java benchmarking tool that also has a C version. <br />
<br />
Running
these two versions on one of my older computers (AMD Athlon II 3200)
showed that Java (open_jdk_1.7 in this case) won by a small margin even
when using profiling and various GCC compiler options for performance.
This result surprised me since the Java JVM is in C++ and seems to be
compiled with GCC/G++ in this case - ok, yes, the JVM can do JIT (just
in time compilation - runtime based optimizations), but to actually beat
C was surprising. (Not all JVMs are written in the same language - some
are C, an early IBM version was in Smalltalk. GCC is written in C++
since 2012, so, I'm using C/C++ a little interchangeably.)<br />
<br />
The
slowest C benchmark was the SOR method with the C version getting ~560
and the Java version about 780 - the other micro-benchmarks were either
in C's favor or equal. Looking at the SOR.c file, it had a simple loop
over three vectors in a 2D matrix/array. There was little to improve on
... at first sight and I suspect this is why Java was winning as it was
doing optimizations that GCC wouldn't produce despite options such as
-funroll -loops. Manually unrolling the loop by a factor of two so that
every loop iteration performed two SOR steps raised the C version
performance to 3200 and unrolling the loops to 4 steps per iteration
raised the performance to 4800. The lesson is clear that with a little
experience in optimizing C, turning a well-written piece of code into a
fast piece of code can be quite easy. Did loop unrolling make a
difference with Java? Yes, but only a little. The Java version was
improved up to 800 (from ~780), but that's a small difference and hardly
worth the tuning effort as Java really did do most of the work for you.<br />
<br />
Now,
what does this tell you - C is probably still the champion after all a
Java program is converted to Java bytecode which is then run on a JVM
which itself is a C++ program - to make Java truly faster than C/C++ is to
make C/C++ faster than C/C++. The difference is that the C++ program called
the JVM puts more effort into optimizing itself than does 'run of the
mill' C (and GCC compilations). If you know and are willing to optimize
C, you'll most likely get some benefit, if not Java may be a good
choice!<br />
<br />
What does all of this tell you - that benchmarks are
easily twisted to suit what you want to say if you're just willing to
find the right one. Anyway, have some fun with the benchmarks and
results above to tell your own story.jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-86046124386581911182019-01-03T21:30:00.000-08:002019-09-16T12:26:03.474-07:00Ansible ExamplesAnsible is an excellent tool for system automation. It’s similar in some ways to Puppet, Chef, and Salt. However, unlike Puppet and Chef, Ansible doesn’t require a local agent to be running on the system being managed. This is an advantage as it saves time and initial configuration (of course, there are reasons that you might want a local agent running…). By the way, the name Ansible comes from the book Rocannon’s World by Ursula K LeGuin.<br />
<br />
Ansible uses the idea of playbooks to coordinate activities - a playbook might have several plays and they could be chosen individually or in some combination. Plays contain tasks which contain modules. Roles are another fundamental concept in Ansible and refer to the way a system might be used or configured. Thirdly, there are hosts. So, a playbook like “webservers.yml” would use the “webservers” role to make the “webservers” hosts (the target hosts) into web servers. You might also have a “base” role that you’d want to be applied to the “webservers” first to get them into a basic configuration.<br />
<br />
Let’s start with listing out the hosts that we want to manage. Create an ansible directory and cd into it:<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;"><span style="background-color: black;"><span style="color: white;">mkdir ansible && cd ansible</span></span></span></span><br />
<br />
Create and edit a host file and put in entries like this:<br />
<span style="color: white;"><span style="background-color: black;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">192.168.0.22</span></span></span></span><br />
<span style="color: white;"><span style="background-color: black;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">localhost</span></span></span></span><br />
<br />
That adds two hosts to our list - localhost and another server at 192.168.0.22<br />
<br />
To make sure that’s right, run this command to list the hosts:<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;"><span style="background-color: black;"><span style="color: white;">ansible all --list-hosts -i ./hosts #list all hosts using the hosts file</span></span></span></span><br />
<br />
Normally, Ansible likes to use /etc/ansible/hosts as its file, but I’d like to keep it local to the work.<br />
<br />
To make sure that everything can communicate correctly, try running this:<br />
ansible all -m ping -i ./hosts #this runs an ad hoc ansible command using the ping module<br />
Output:<br />
<span style="background-color: #38761d;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">192.168.0.22 | SUCCESS => {</span></span></span></span><br />
<span style="background-color: #38761d;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;"> “ansible_facts”: {</span></span></span></span><br />
<span style="background-color: #38761d;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;"> “discovered_interpreter_python”: “/usr/bin/python”</span></span></span></span><br />
<span style="background-color: #38761d;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;"> },</span></span></span></span><br />
<span style="background-color: #38761d;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;"> “changed”: false,</span></span></span></span><br />
<span style="background-color: #38761d;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;"> “ping”: “pong”</span></span></span></span><br />
<span style="background-color: #38761d;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">}</span></span></span></span><br />
and another (uses the default command module):<br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">ansible all -a /bin/date -i ./hosts #this runs a single command rather than a module</span></span></span></span><br />
<br />
By the way, if you haven’t set up your SSH keys and want to use a simple username and password pair, try this:<br />
<span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"><span style="color: white;"><span style="background-color: black;">ansible all -a /bin/date -i ./hosts -u username -k #set username to the username you want to use</span></span></span></span><br />
<br />
When the remote system doesn’t have Python installed, it's useful to use the raw module:<br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">ansible all -m raw -a “ls -l”</span></span></span></span><br />
<br />
One that shows all of the ansible_facts visible by default is:<br />
<span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"><span style="background-color: black;"><span style="color: white;">ansible all -m setup -i ./hosts #loads of info (use as {{ ansible_facts[‘node’] }}</span></span></span></span><br />
<br />
Ansible also has a flag to test the actions without running them: -C. The above example with /bin/date <br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">ansible all -C -a /bin/date -i ./hosts </span></span></span></span><br />
Instead of connecting to the remote system and showing the date, it produces:<br />
<span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"><span style="background-color: #38761d;"><span style="color: white;">192.168.0.22 | SKIPPED</span></span></span></span><br />
<br />
Let’s update the hosts file to group the host(s).<br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">---</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">[cloudserver]</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">192.168.0.22</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">[local]</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">locahost</span></span></span></span><br />
<br />
Test this if you want by running something like this:<br />
<span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"><span style="color: white;"><span style="background-color: black;">ansible cloudserver -m ping -i ./hosts</span></span></span></span><br />
<br />
One-off commands are fun, but the power of ansible comes from the playbooks. Here’s a simple playbook, let’s call it simple_playbook.yml<br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">---</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">- name: check ping and date</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> hosts: cloudserver</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> remote_user: username</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> vars:</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> file_value: “Here are the file contents”</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> </span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> tasks:</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> - name: get date</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> command: /bin/date</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"><br /></span></span></span></span>
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> - name: copy file to remote server using template</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> template: src=template/play_file.j2 dest=./play_file.txt</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"><br /></span></span></span></span>
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> - name: list files</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> shell: ls -l play_file.txt</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"><br /></span></span></span></span>
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> - name: start http</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> service: name=httpd state=running</span></span></span></span><br />
<br />
Create a local directory called template and add a file to it called play_file.j2. In that file, add a Jinja2 variable placeholder:<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;"><span style="background-color: black;"><span style="color: white;">{{ file_value }}</span></span></span></span><br />
<br />
Run this playbook:<br />
<span style="color: white;"><span style="background-color: black;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">ansible-playbook simple_playbook.yml -i ./hosts -k #adding the -k option in case SSH keys aren’t set</span></span></span></span><br />
<br />
If you want to check the syntax first, run this:<br />
<span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"><span style="color: white;"><span style="background-color: black;">ansible-playbook simple_playbook.yml -i ./hosts --syntax-check</span></span></span></span><br />
<br />
There’s also a --verbose option that will add much more detail to the output.<br />
<br />
That is it; a short set of examples of Ansible. <br />
<br />jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-82576902237377895342017-10-03T14:05:00.202-07:002021-10-04T02:59:26.887-07:00Many Slow Consumers All At Once On ActiveMQ<p><b><span style="font-size: medium;"> Many Slow Consumers All At Once On ActiveMQ</span></b></p><p>In a previous post, we discussed consumer buffering and caching. We had to go a little deeper into that when we noticed that several of our consumers seem to all be running slowly when the entire system (broker, producers, and consumers) were all under extremely heavy load.<br /></p><p> <img alt="" src="data:image/png;base64,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hYQHSIAESCBTCDBtIFOwc1ASIAESIAESIAESIAFvCNB59YYaryEBEiABEiABEiABEsgUAtp57dKli96IQF5Z1b6FldRChbI+q6zzasT2nVpBvF27dihZsiQKqL0Y69Wrp3cCy4h2/fp1td+5gw3PM0IBjkECJOATAnv27FFruT6u75vrZYusO+3WrVtqzdd3ERERgTx58qB8+fJ6ExfZBIaNBEiABEgg7QSskdc+ffqohbMv4OTJk5g9e7Zek1Wcunnz5qV9FD+S0LVrV/0PiTjsK9Tm4r///jv+7//+T2+6kBHt7bffxosvvpgRQ3EMEiCBdCBw/Phx9OjRQ2/gMn/+/FQjDFLbYw0ZMgQ3btxAx44d9Y/V999/H717907VlwdIgARIgAQ8J2Bd5zVnzpw6CimtaNGiaNq0KUqpvQ379euHhx9+GPnz5/dcup9dITt8rVH7MP71118p7KlVq5afaUp1SIAE/JXAALVV1vdqO6z7779fb6ctEVhLkz1foqKiEBoaih07duj7jDi7FSpUwJIlS/zVJOpFAiRAAgFFwGnOq0QQbt68icWLF2uj5L0cK1OmjN5GVqK1yR+FyZayQ4cORbly5XSfV9XehqVLl8aBAwf09eHh4Vi5cqUVkGwnKzd5S3MlP63XS7WwvJw54klJSRg/frze3laiz+3bt8fhw4etOrrSoX79+njnnXfQrFkzPU6dOnWwYcMGff17772HcePGYe7cuciePTv69u1rlcs3JEACgUFgzJgx+O233/T/15UrV06ltGyVffv2beu9UVKxxKkVZ5eNBEiABEgg7QScOq+SrxUZGYno6Gg9kkRhDx06hK1bt6p9tv/A/v37MWzYMKsW8qhs2bJlWLBgAVatWqVTECTq4G5zJd+VHGfXi5MdExODRo0aORWj1r3Fjz/+qPYd/wHbtm1Djhw50KpVK+24u9POnj2rmchjQuHToEEDPPXUUxDHXtIVRqhNyzt37gzJi/v000/dEck+JEACfkRAcllbtGhhVyPJf+3Vq5dOFahRo4b+MS9Pse69917945WNBEiABEgg7QScOq8iXoqaxCG7dOmSLmqaPHkyihcvrlMKxo4da835kkIkccakjzyGr1ixokdFUK7kuzLV1fUHDx7U0Q9JiXDUJEftww8/1HpXr15dP+r76quvtO0LFy50dFmK45I7KznDDRs21JzEYY2Li7NGn90Swk4kQAIBS+Ctt97CQw89hPj4eEyYMEH/vy/OrjyNYiMBEiABEkg7AWvOqyNR4ng1btxYR1/lkfoDDzygq2ulSfTQkjYgN2hxDuUxuaXJ4zJ3myv5ruS4ul4e90sTPQsXLmxXnDi48rivdu3a1vN58+bV0dq9e/favcbewSxZ7v4mEAdY8omvXLliryuPkQAJGIiA3CMfffRRrF69Wv8QLlu2rP47Y8YMnWrw559/Qu4pbCRAAiRAAt4TcOq8JiQk4O+//4Y8SrcUc23fvh1FihRJNaI8VhfHT27ezpxWcXDtNVfyLdd4e71ERCUS+uuvv0LyUu01iR7bs0FSB3LlymW9xJEO9mTKMYuz7+g8j5MACRiDgKxgIjUCTz/9NAYOHKiNeuKJJ3TRq6RUyUuW12IjARIgARLwnoDTtIFJkybpwqWWLVvqIiwprnL0+FzSBMSpW7t2rVUbWydPIg62EUxxdqW5ki990nq9/GMieWcSYbXX7NkguaqS+yr5a650sCfT3jGJWLORAAkYj4CkHkmLjY3VP+SlyY9Xy9OY3LlzG89oWkQCJEACGUzA6rxK5FRSAGTpFynI6t+/P6ZMmYJp06bpm69Ux8tKA2+88QaWL1+uC5COHTsGicRKE8dW1lCVtQy3bNmi8zylf/ImKQeSD3rkyBH9+L5nz57WBftdyRc5ab1elripW7eufkl+7u7du3U6xLfffotFixZpG7p3765tEAanTp3SHCRi26ZNG22KMx1SGOvgg6y+IGvLCgMuWu4AEg+TQIASkNxW2ZRg3bp1ul5A1pOW+83SpUt18ausQsJGAiRAAiSQRgIqOmpS1e/yLF+/1CNyk8r5NKllnEyq4EBOW5uKJJhUFb1JVduaVBTUpG7SJpXLZT2vnDGTqrQ1qWW0TFWrVjW9+eabWqaq8td91OoDprZt25rUUjImtcC3SS3Yb1L5p27LT+v1MpBKCzB99NFHpiZNmphUqoIeX96rwiyth0qVMKlVC0zqHx5tx7PPPmtSBWtWHV3poDZ2MKl1Hq395Y1KOTCp6K0+pqqQTSonTjNQy3Cl6McPJEAC/ktApU+lUu6xxx7T9zjlrFrPHT161KQ2MTCpfFf9/77cJ+V+euLEiVTX8wAJkAAJkIDnBELkkjT6vw4vl8iirHkoS1RJ4RIbCZAACQQqAVkjeuTIkYGqPvUmARIgAcMQcJrzahgraQgJkAAJkAAJkAAJkIAhCPw/8aDSKobiGgcAAAAASUVORK5CYII=" /></p><p>The initial thought was that we'd reached a hard limit in our system - max CPU, memory or even disk IO. However, none of those showed any real signs of stress or high values. </p><p>Digging a little deeper it seems that we have a few slow consumers, but that has always been the case without causing impact on other queues or consumers. </p><p>Combining the two issues and looking even deeper into the memory usage limits and consumer behaviors, we started to see a problem: heavy load <i>plus</i> a few high volume, slow consumers <i>can</i> cause a knock-on effect to many other consumers.</p><p>The key background has to do with the ActiveMQ memory limits and the way that the destination (queue) cursor works. ActiveMQ has a global memoryUsage setting - this is the max space it can use. It typically doesn't let you use it all as it needs to reserve some to make sure that the broker has enough space as keeping the broker up is more important. </p><p>Meanwhile, incoming messages for a persistent queue get assigned memory in the destination cursor. A somewhat healthy cursor looks like the image above - where it goes up and down as the messages come in and get read off. The image is from a test case of limited memoryUsage and large messages (and some settings described below) so one takes a decent amount of the overall space available. </p><p>In order to preserve the broker, the cursors are generally limited to only 70% of the broker memory usage. Once that has been hit, the cursors for destinations and consumers can be blocked on taking and sending more messages until the slow queues have reduced usage. Again, this shouldn't be a problem most of the time, especially on a broker with fast consumers or small, slow consumer queues. However, if a few slow consumers build up a large backlog, then it can affect all consumers slowing them down to the clearing rate of the slow consumers (btw, I'm talking about persistent queues here - non-persistent queues, topics, and older versions (before 5.5?) of ActiveMQ can have different behavior).<br /></p><p>What's the fix? Basically, the fix is to limit the slow consumers from hogging the memory so there are a few things. Again, the memory allocated for all queue cursors is 70% of the global memoryUsage limit. One option is to increase that limit - this turns out to be the easiest way to deal with it!</p><p>Another option is to impose memory limits per queue. This is best done with memoryLimit (often in conjunction with producer flow control) like this:</p><p><span style="background-color: black; font-size: small;"><span style="color: white;"><span style="font-family: courier;"><span><policyEntry queue=">" cursorMemoryHighWaterMark="50" memoryLimit="4 mb" producerFlowControl="true" ></span></span></span></span></p><p><span style="background-color: black;"><span style="color: white;"><span style="font-family: courier;"><span style="font-size: x-small;"><span style="font-size: small;"></policyEntry? </span><br /></span></span></span></span></p><p>Noticed that we've also added the cursorMemoryHighWaterMark - this defaults to 70% - it is the value that sets the 70% of the memoryUsage value. When the memoryLimit is added, it should then be applied to that memoryLimit rather than the global memoryUsage. In the above xml, we've applied it to all queues, but that could be changed to known slow queues. The memoryLimit and cursorMemoryHighWaterMark are not normally applied so that queues have the flexibility to use what they need. Producer flow control is on by default, btw. More details on these settings can be found here on the <a href="https://activemq.apache.org/per-destination-policies" target="_blank">per-desitination-policies</a>.<br /></p><p>We aren't the only ones that have come across this problem - an ActiveMQ committer <a href="https://github.com/tmielke/abloggerscode/tree/master/Articles/Blog/StuckConsumerActiveMQ" target="_blank">posted a test case</a> so that you can try out various settings (some updates such as the pom.xml source/target and ActiveMQ version might be needed). </p><p>Our tests showed that the best and simplest solution was to increase memoryUsage. However, if that isn't workable or you want a more targeted solution, then memoryLimit and cursorMemoryHighWaterMark settings were good and even more so if you could target the specific slow queues. You'll see producer flow control kick in. </p><p>One interesting point to note though is that by watching the JMX MBeans, it was clear that the slow consumer in the above test case still consumed a much larger portion of memory than these settings should allow. So, we'll need to dig into the code more to understand the workings and why that happens. However, the memory limits and high water marks should help. Btw, we also noticed differences in behavior due to differences in ActiveMQ version, but that is to be expected. </p><p>If this still isn't enough, there are a few other settings to try on the queues. We tried <i>prefetch</i> and store cursor (<span style="font-family: courier;"><span style="font-size: x-small;"><pendingQueuePolicy> <storeCursor/> </pendingQueuePolicy></span></span>) not to make a difference. These combined settings: might make a difference, but we didn't observe any in a simple test.<br /></p><p>There's also a setting to try if none of the above is enough. At the broker level, the memory used by producers and consumers is shared/common. However, that can be split out using this setting: splitSystemUsageForProducersConsumers. This setting at the <broker> level splits the memory between the producter and consumers - 60/40 by default: </p><p><span style="background-color: black; font-size: small;"><span style="color: white;"><span style="font-family: courier;"><span><broker splitSystemUsageForProducersConsumers producerSystemUsagePortion="60" consumerSystemUsagePortion="40" ></span></span></span></span></p><p>We didn't try that approach, but might come back to it if needed.<br /></p><p>A few references if it helps:</p><p><a href="https://blog.christianposta.com/activemq/activemq-understanding-memory-usage/" target="_blank">AMQ Memory</a><br /><a href="https://dzone.com/articles/activemq-understanding-memory" target="_blank">AMQ understanding memory</a></p><p><span style="color: #666666;"><a href="https://dzone.com/articles/starving-jms-consumers-in-activemq" target="_blank">Starving AMQ consumers </a></span><br /></p>jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-90983016900759037512017-03-30T03:34:00.000-07:002019-03-30T03:34:48.837-07:00When Apache webserver won't start & semaphoresOur alerting system started telling us one of our Apache systems
(Graphite front end) wasn't running. After a while I joined in the
diagnosis of what why it still wasn't running and how to get it running
again. Trying to restart Apache only produced this:<br />
<br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">service httpd start ; tail -f error_log</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">Starting httpd: [ OK ]</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">[Tue Mar XX 06:21:41 2016] [notice] Digest: done</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">Configuration Failed</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">[Tue Mar XX 06:23:33 2016] [notice] suEXEC mechanism enabled (wrapper: /usr/sbin/suexec)</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">[Tue Mar XX 06:23:33 2016] [notice] Digest: generating secret for digest authentication ...</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">[Tue Mar XX 06:23:33 2016] [notice] Digest: done</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">Configuration Failed</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">[Tue Mar XX 06:24:39 2016] [notice] suEXEC mechanism enabled (wrapper: /usr/sbin/suexec)</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">[Tue Mar XX 06:24:39 2016] [notice] Digest: generating secret for digest authentication ...</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">[Tue Mar XX 06:24:39 2016] [notice] Digest: done</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">Configuration Failed</span></span></span></span><br />
<br />
It
was looking fine for a fraction of a second and then died with
"Configuration Failed". A quick check of apachectl -t showed that the
conf file was fine and that no one had changed it, so it wasn't
referring to that. I also checked directory permissions, disk
space and ionodes available - all looked fine. One option was that we
could move all of the conf/vhosts files and try a clean start, but that wouldn't explain the problem.<br />
<br />
Going deeper instead, I ran strace (it's possible I could have used httpd -e DEBUG instead of strace):<br />
<br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">strace -f -o /tmp/apache.trace /usr/sbin/httpd</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"><br /></span></span></span></span>
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 read(10, "FGF95a\t\timage/unknown\n#\n# GRR 95"..., 4096) = 4096</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 read(10, " The contributor claims:\n# I c"..., 4096) = 851</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 read(10, "", 4096) = 0</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 close(10) = 0</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 open("/etc/httpd/conf/magic", O_RDONLY|O_CLOEXEC) = 10</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 fcntl(10, F_SETFD, FD_CLOEXEC) = 0</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 read(10, "# Magic data for mod_mime_magic "..., 4096) = 4096</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 read(10, "o figure out what's inside.\n\n# s"..., 4096) = 4096</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 read(10, "FGF95a\t\timage/unknown\n#\n# GRR 95"..., 4096) = 4096</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 read(10, " The contributor claims:\n# I c"..., 4096) = 851</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 read(10, "", 4096) = 0</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 close(10) = 0</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 write(2, "[Tue Mar 07 00:29:06 2017] [noti"..., 92) = 92</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 open("/dev/urandom", O_RDONLY) = 10</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 read(10, "#\27\356'm[7T\266\265\373\374\203\16/_\375\236\10\200", 20) = 20</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 close(10) = 0</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 write(2, "[Tue Mar 07 00:29:06 2017] [noti"..., 49) = 49</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 mmap(NULL, 500008, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0) = 0x7f1cd2f02000</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 semget(IPC_PRIVATE, 1, IPC_CREAT|0600) = -1 ENOSPC (No space left on device)</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 write(2, "Configuration Failed\n", 21) = 21</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 select(0, NULL, NULL, NULL, {0, 10000}) = 0 (Timeout)</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 close(9) = 0</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 close(8) = 0</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 close(7) = 0</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 munmap(0x7f1cd2f02000, 500008) = 0</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 close(6) = 0</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 close(5) = 0</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 munmap(0x7f1cc8690000, 2248032) = 0</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">34583 munmap(0x7f1cc82ea000, 3823040) = 0</span></span></span></span><br />
<span style="color: white;"><span style="background-color: black;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">....</span></span></span></span><br />
<br />
That
looked weird. These two lines were interesting:<br />
<span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">34583 semget(IPC_PRIVATE, 1, IPC_CREAT|0600) = -1 ENOSPC (No space left on device)</span></span></span></span><br />
<span style="background-color: black;"><span style="color: #f3f3f3;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">34583 write(2, "Configuration Failed\n", 21) = 21 </span></span></span></span><br />
<br />
Checking
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;"> ipcs -s</span></span></span></span> showed a large number of apache owned semaphores still in the
system. Since apache wasn't running and wouldn't run, we decided to
clear these down with a simple:<br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">ipcs -s | grep apache | awk '{print $2}' | while read id</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">do</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;"> ipcrm sem "$id"</span></span></span></span><br />
<span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">done</span></span></span></span><br />
<br />
This
cleared up the semaphore list and allowed apache to restart. Under
normal working conditions, apache only uses 2-4 semaphores. Looking on
the web, this appears to be related to abrupt stops of Apache. Off to
check if the team had been doing this and not realizing :)<br />
Hope this helps anyone else trying to solve this problem.jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-29065934997713841732016-03-03T03:02:00.000-08:002019-03-30T03:23:14.521-07:00Graphite ClustersGraphite is a great time series tool. It's losing momentum to
the newer players in the field like Prometheus (more on this on in
another blog), Influxdb, etc. However, for a few
hundred thousand to a few million metrics, Graphite can be an easy
choice. Also, for some use cases, such as business metrics Graphite is
the better choice. That's because Graphite allows you to update old
records (say you found an error and the business needs to know the right
story) while most of the other solutions only allow you to write data
once - what you knew at the time is all you get.<br />
<br />
Graphite
installation is fairly straight forward on RedHat and Ubuntu setups although a few tweaks may be necessary (see this <a href="https://www.randomparallels.com/2013/08/using-graphite-metrics-data.html">post</a>). We use Apache HTTP as the web server for the python front end; make sure WSGI or the python gateway uses the same major version of python as the graphite install did (i.e. python 2 or 3).<br />
<br />
To use graphite, send metrics to port 2003 and start viewing them via the UI at port 80. To send metrics to the text port 2003 do something like this from a Linux/Mac command line:<br />
<span style="background-color: black;"><span style="color: white;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">echo "mymetric.somestat.count 342 1489003770" | nc graphite_host 2003</span></span></span></span><br />
This creates or adds a value for the metric "count" of "somestat" which is a subset of "mymetric" (application, for example). Graphite also listens on 2004 for data in a pickle format for sending bulk data - it's faster as well.<br />
Here's a flow of data for the simple Graphite setup:<br />
data ingestion: data -> carbon-cache (line fed: port 2003; pickle: port 2004) -> stored in its cache -> writes out to whisper files<br />
graphite-web request pulls from: whisper files & carbon-cache port 7102 for cached data<br />
<br />
<br />
That's
great and with reasonable hardware (a few cores, 4-8GB, decent disks) you can scale it up to about
200k-300k metrics without any real effort. To see the performance of the metric recorder carbon-cache, keep an eye on the carbon metrics in the graphite
web front end. Also, pay attention to apache's memory demands and IO
and CPU load.<br />
<br />
When more metrics or more dashboards on the metrics are needed, it's time to cluster graphite or at least scale out. Here's a quick overview:<br />
Use
carbon-relay(s) to send metrics to more than one carbon-cache instance
which will store them. This can be done in replicated mode (all metrics
to multiple hosts) or in a load sharing fashion (metric 1 to host A,
metric 2 to host B, etc). Use graphite-web to read local metrics from
the whisper files written by carbon and from the data in carbon-cache's
cache. Then, if wanted, use a global graphite-web to read from the local
graphite-webs. Add memcache to speed things up by storing the images
and data coming out of the graphite-webs. There's one extra piece which
is the carbon-aggregator which can take a group of related metrics and
write them out as an aggregate (sum, ave, etc) to reduce the original
storage needs (or add a little to them).<br />
<br />Here's a flow for feeding data into a cluster:<br />
data
ingestion (port 2013 (linefeed)/2014 (pickle): 1 or more carbon-relays
-> using hashing, rules, or replication setting -> multiple
carbon-caches (pickle format on port 2004 (or multiple ports for multiple
instances on one host)) -> store in cache -> whisper files<br />
Viewing/retrieving data:<br />
graphite-web -> other graphite-webs in cluster -> read local whisper files and local carbon-cache cached data<br />
<br />
The configuration for these cluster settings is in the carbon.conf file <br />
<br />
It can get a little more involved when setting up the various components and making sure that files refer to the information.<br />
<br />
<span style="color: #f3f3f3;">Helpful links:</span><br />
<span style="color: #f3f3f3;">http://bitprophet.org/blog/2013/03/07/graphite/</span><br />
<span style="color: #f3f3f3;">https://rcrowley.org/articles/federated-graphite.html</span><br />
<span style="color: #f3f3f3;">http://allegro.tech/2015/09/scaling-graphite.html</span><br />
<span style="color: #f3f3f3;">https://grey-boundary.io/the-architecture-of-clustering-graphite/</span>jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-15009622665005648302015-09-28T23:53:00.001-07:002019-03-03T03:00:01.541-08:00Replicated LevelDBWe've recently decided to upgrade our ActiveMQ instances and thought we'd try replicated LevelDB with the hope that it is the answer to our shared storage problems (shared SQL store is too slow (on fast SQL Servers) and shared locking on NFS didn't lock). We used three ActiveMQ brokers and three ZooKeeper instances. The general setup and configuration followed the <a href="http://activemq.apache.org/replicated-leveldb-store.html">Replicated_LevelDB page</a>. We also used HAproxy (or use another load balancer) as an interface to our front end so that one URL would support all the brokers. There was one feature that we were <b>not </b>able to run with replicated LevelDB: <a href="http://activemq.apache.org/delay-and-schedule-message-delivery.html">delayed message queuing</a>, but we could live with that.<br />
<br />
This 3 node replicated configuration worked fine for weeks although occasionally one of the nodes would fail and we'd need to bring it back. The logs indicated that it'd timed out and shut down. Then we had a cascade of these shutdowns - node 1 would fail, then restart, then node 3, then node 2 - they were good in that they tried restarting, but after a few minutes of rapid failover and recovery, the nodes gave up and shut down entirely often leaving one node up, but not the master since a quorum wasn't available.<br />
<br />
After restarting the nodes to get the system running again, we checked the logs and saw messages about ZooKeeper timeouts on the order of a few seconds (2-3secs). These nodes are all in the same rack in the same data center - network times should always be 1-2ms (sometimes spiking at 7-10ms), not 1000 times higher. We left the cluster again to observe this again. Within a month, it happened again with very much the same situation. For operational sanity, we turned off replicated LevelDB and are back to a non-HA solution while we investigate the solutions. To put this in perspective, the problem could be our ZooKeepers and set up - we have had to put effort into tuning ZooKeeper in the past. The ActiveMQ guys also mention (can't find the link now) that replicated LevelDB is cutting edge and might not be ready for full production use.jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-57715585109698542942015-05-15T13:53:00.197-07:002022-08-29T00:20:00.362-07:00ActiveMQ Moving Several Queues at Once - Example<p><span style="font-size: x-small;">(Update in 2022 for log4j)</span></p><p>There are times that a large number of queues need to be moved from one broker or another - or upload from/download to files.</p><p>One example is DLQs building up and taking too much storage space or something going wrong and needing to recover an ActiveMQ instance that had a load of messages on it. Here are a couple of scripts used to find all queues with messages without consumers and convert it into the Java Camel from-to statements in the code below. It uses the AMQ UI to get the data since using jolokia required more steps but it might be cleaner to use jolokia!</p><p>Get the queues:<br /></p><p><span style="font-family: courier;"><span style="font-size: x-small;"><span style="color: white;"><span style="background-color: black;">curl -o queues http://0.0.0.0:8161/admin/xml/queues.jsp<br />tr -d "\n" < queues | sed "s/<queu/\n<queu/g" | grep -v size=\"0 | grep consumerCount=\"0 | sed 's/\"> /\"\/> /' | cut -d" " -f1,2 | cut -d\" -f2 </span></span></span></span></p><p>Now, how to move all of the messages... (btw, there are other ways like adding bridges). Camel makes this fairly easy - all you need is a Java "wrapper" to run the camel config. Of course, there are two ways of doing this: camel config in its own file or camel used in Java files.</p><p>This example uses a camel example, along with some linux commands, to find and move messages from/to queues.<br /></p><p>Setup a directory (such as TestCamel) with this directory under it (use IntelliJ or Eclipse to make it easier): <br /><span style="font-size: x-small;"><span style="font-family: courier;"><span style="background-color: black;"><span style="color: white;">mkdir -p src/main/java</span></span></span></span></p><p>Add a Java file in that src/main/java directory, I've called my TestCamel.java:</p>
<p><span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: courier;">package com.testcamel;<br /><br />import javax.jms.ConnectionFactory;<br />import org.apache.activemq.ActiveMQConnectionFactory;<br />import org.apache.camel.CamelContext;<br />import org.apache.camel.component.jms.JmsComponent;<br />import org.apache.camel.impl.DefaultCamelContext;<br /><br />public class TestCamel {<br /><br /> public static void main(String[] args) {<br /> BasicRB rB = new BasicRB();<br /> CamelContext ctx = new DefaultCamelContext();<br /> <br /> ConnectionFactory connectionFactoryFrom = new ActiveMQConnectionFactory("tcp://0.0.0.0:61616");<br /> ctx.addComponent("jmsFrom", JmsComponent.jmsComponentAutoAcknowledge(connectionFactoryFrom));<br /><br /> ConnectionFactory connectionFactoryTo = new ActiveMQConnectionFactory("tcp://10.10.0.38:61616");<br /> ctx.addComponent("jmsTo", JmsComponent.jmsComponentAutoAcknowledge(connectionFactoryTo));<br /> <br /> try {<br /> ctx.addRoutes(rB);<br /> ctx.start();<br /> Thread.sleep(5000);//give it 5 seconds to work<br /> ctx.stop();<br /> }<br /> catch (Exception e) {<br /> e.printStackTrace();<br /> }<br /> }<br />}</span></span></span></span><br /></p>
<p>Add another Java file, I've called it BasicRB.java for basic camel route builder:</p>
<p><span style="font-family: courier;"><span style="font-size: x-small;"><span style="background-color: black;"><span style="color: white;">package com.testcamel;<br /><br />import org.apache.camel.builder.RouteBuilder;<br /><br />public class BasicRB extends RouteBuilder {<br /> @Override<br /> public void configure() throws Exception {<br /> from("jmsFrom:queue:Activemq.DLQ").to("jmsTo:queue:reprocessing_application_10.queue");<br /> }<br />}</span></span></span></span></p>
<p>Add this pom.xml to the top level directory (above src/):</p> <span style="font-family: courier;"><span style="font-size: x-small;"><span style="background-color: black;"><span style="color: white;"><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br /> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><br /> <modelVersion>4.0.0</modelVersion><br /> <groupId>com.testcamel</groupId><br /> <artifactId>camel-test-activemq</artifactId><br /> <version>0.0.1-SNAPSHOT</version><br /><properties><br /> <maven.compiler.source>1.8</maven.compiler.source><br /> <maven.compiler.target>1.8</maven.compiler.target><br /></properties><br /> <build><br /> <plugins><br /> <!-- any other plugins --><br /> <plugin><br /> <artifactId>maven-assembly-plugin</artifactId><br /> <executions><br /> <execution><br /> <phase>package</phase><br /> <goals><br /> <goal>single</goal><br /> </goals><br /> </execution><br /> </executions><br /> <configuration><br /> <descriptorRefs><br /> <descriptorRef>jar-with-dependencies</descriptorRef><br /> </descriptorRefs><br /> </configuration><br /> </plugin><br /> </plugins><br /> </build><br /> <dependencies><br /> <dependency><br /> <groupId>org.apache.camel</groupId><br /> <artifactId>camel-core</artifactId><br /> <version>2.14.2</version><br /> </dependency><br /> <dependency><br /> <groupId>org.apache.camel</groupId><br /> <artifactId>camel-jms</artifactId><br /> <version>2.14.2</version><br /> </dependency><br /> <dependency><br /> <groupId>org.apache.activemq</groupId><br /> <artifactId>activemq-camel</artifactId><br /> <version>5.10.2</version><br /> </dependency><br /> <dependency><br /> <groupId>org.apache.activemq</groupId><br /> <artifactId>activemq-broker</artifactId><br /> <version>5.10.2</version><br /> </dependency><br /> <dependency><br /> <groupId>org.apache.activemq</groupId><br /> <artifactId>activemq-client</artifactId><br /> <version>5.10.2</version><br /> </dependency><br /> <dependency><br /> <groupId>org.apache.activemq</groupId><br /> <artifactId>activemq-pool</artifactId><br /> <version>5.10.2</version><br /> </dependency><br /><!-- https://mvnrepository.com/artifact/javax.jms/javax.jms-api --><br /><dependency><br /> <groupId>javax.jms</groupId><br /> <artifactId>javax.jms-api</artifactId><br /> <version>2.0.1</version><br /></dependency><br /><!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl --><br /><dependency><br /> <groupId>org.apache.logging.log4j</groupId><br /> <artifactId>log4j-slf4j-impl</artifactId><br /> <version>2.18.0</version><br /> <scope>test</scope><br /></dependency><br /><!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-simple --><br /><dependency><br /> <groupId>org.slf4j</groupId><br /> <artifactId>slf4j-simple</artifactId><br /> <version>2.0.0-alpha7</version><br /> <scope>test</scope><br /></dependency><br /> </dependencies><br /></project></span></span></span></span><br />
<p>Then you should be able to run:</p><p><span style="font-family: courier;"><span style="font-size: x-small;"><span style="color: white;"><span style="background-color: black;">mvn package</span></span></span></span></p><p>Which will kick off the compile and bundling of the code into a jar in the target/ directory</p><p>To run it is fairly straight forward - add the target to the classpath. It runs for 5 sec and then stops which might be enough time to move all of your messages. </p><p><span style="background-color: black;"><span style="color: white;"><span style="font-size: x-small;"><span style="font-family: courier;">java -cp camel-test-activemq-0.0.1-SNAPSHOT-jar-with-dependencies.jar com.testcamel.TestCamel</span></span></span></span></p><p>As mentioned above, there are a few other options for moving messages temporarily - bridged queues from one broker to another or camel config in ActivMQ (will add that later) or pulling and pushing messages via the APIs or ActiveMQ cli consumer/producer. For longer term moving of messages and depending on your use case, virtualtopics and/or AMQ config via bridges or static routes is better.<br /></p><p><br /></p>jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-30595403259550156722015-03-01T23:08:00.000-08:002015-03-01T23:12:18.966-08:00ActiveMQ 6 - ActiveMQ + HornetQHaving a look at the latest ActiveMQ releases (<a href="http://activemq.apache.org/activemq-5110-release.html">5.11</a> specifically although 5.11.1 is out) and the future of the Apollo project which uses a threading and messaging dispatch method (<a href="http://activemq.apache.org/apollo/documentation/architecture.html">based on HawtDispatch the Java port of libdispatch/Grand Central reactor design</a>), we started to wonder about when we'd see something new presumably in ActiveMQ 6. Looks like there is going to be something new coming.<br />
<br />
ActiveMQ 6 looks to be a combination of HornetQ and ActiveMQ with more HornetQ flavor than ActiveMQ. Since we're still using mostly JMS, HornetQ has been our most likely alternative to ActiveMQ should we want to change; HornetQ has also won some speed tests. Turns out the two have more in common than we realized - not only is there a decent amount of overlap in capabilities, but both sets of developers largely work for the same company. Commercially ActiveMQ is part of RedHat's JBoss Fuse ESB and HornetQ is the open sourced child of RedHat's JBoss broker.<br />
<br />
HornetQ's project manager seems to <a href="http://activemq.2283324.n4.nabble.com/Possible-HornetQ-donation-to-ActiveMQ-td4682971.html">have raised the idea</a> and the ActiveMQ dev community seems fairly eager to join forces rather than split mind share. <a href="https://github.com/apache/activemq-6">Commits to ActiveMQ repos</a> for version 6 look to be working through the incorporation of the code although <a href="http://activemq.apache.org/new-features-in-60.html">the page on the official site</a> (at the moment) still mentions Apollo as being the future core of AMQ 6. It also looks like the <a href="https://developer.jboss.org/thread/252146?_sscc=t">HornetQ dev forum</a> are seeing the combination as extremely likely to happen.<br />
<br />
So, what is going on with the Apollo reworking? The main developer <a href="http://activemq.2283324.n4.nabble.com/Possible-HornetQ-donation-to-ActiveMQ-tt4682971.html#a4682972">mentions that it could be the use of Scala that has caused slow uptake</a> by other developers and he was considering porting to Java to fix that. The HornetQ donation may have saved him the work. <br />
<br />
All in all the inclusion of HornetQ plus users or the Apollo core in AMQ 6 would be a good step forward - will have to wait to see what happens. Might put a few experiences with HornetQ on this blog just in case.<br />
<br />
<br />jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-30315906801912101662014-12-16T04:17:00.000-08:002019-03-10T11:19:44.553-07:00Finding old messages in ActiveMQ queuesActiveMQ doesn't have a direct way to find old messages. There shouldn't be old messages really, but sometimes it happens especially when you have a hundred developers running a variety of things and not always cleaning up DLQs or other queues without consumers. Below is some python code to find the old messages. The output of the python script can be used to feed a script (on another blog entry on this site) to pull off the old messages.<br />
<br />
Why would you do this? Well, ActiveMQ holds on to message data files in KahaDB when just one unconsumed message is present. That can lead to disk storage issues which can cause queue producers to block if not managed. We have that problem. Yes, you could do this queue checking via JMX or Jolokia, but we're running a variety of ActiveMQ instances and not all have Jolokia and I wanted to write something that would be easier for Linux sys admins to update if needed (assuming Python was more what they'd like). For using Jolokia and HawtIO check <a href="http://www.christianposta.com/blog/?p=315">this blog</a>.<br />
<br />
Meanwhile, here's the python code regardless of what version of ActiveMQ you're using - but assuming you're running some of the front end resources. <br />
<br />
Here's the code:<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_5OxFjAjNoUHJwT6y2EEeSkzisOZ690MvUfgFs8VyXqs704HQym8Wrsk2aLq0yoFiOIAuSp4XUeyoJUebkx8xwX3uYRv_S7gPe8oeQissml82_XYwtmmn4vGcRvKmN3-97MgFAsJAq-5s/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> import calendar
import sys
import time
from xml.dom.minidom import parse,parseString
import urllib2
if len(sys.argv) < 3:
print "run with parameters: activemq_url(without :8161) time_in_secs_in_past"
sys.exit(0)
else:
url_root=sys.argv[1]
time_delta=int(sys.argv[2])
print "Running with: ",url_root,time_delta
url=url_root+':8161/admin/xml/queues.jsp'
print url
try:
dom=parse(urllib2.urlopen(url))
except:
print "error pulling queue list, stopping"
sys.exit(2)
queues_msgs={}
queues_old_msgs={}
#dom=parse('queues.jsp')
for node in dom.getElementsByTagName('queue'):
con_count= int(node.childNodes[1].getAttribute('consumerCount'))
queue_size= int(node.childNodes[1].getAttribute('size'))
queue_name= str(node.getAttribute('name'))
# print queue_name,con_count,queue_size
if queue_size > 0 and con_count < 1 :
print "queue with messages and no consumers: ",queue_name,queue_size
queues_msgs[queue_name]=queue_size
#now check each queue for old messages
ts=calendar.timegm(time.gmtime()) #get current time
#get timestamp for X days ago:
delta=time_delta
ts_old=ts-delta
for k in queues_msgs.iterkeys():
count=0
url=url_root+':8161/admin/queueBrowse/'+k+'?view=rss'
try:
queue_dom=parse(urllib2.urlopen(url))
for node in queue_dom.getElementsByTagName('pubDate'):
time_str= node.childNodes[0].nodeValue
# example: Tue, 16 Dec 2014 08:30:44 GMT
time_tup=time.strptime(time_str,'%a, %d %b %Y %H:%M:%S %Z')
time_secs=int(time.mktime(time_tup))
if time_secs < ts_old:
count+=1
print "old msg on queue:",k,"msg time: ",time_str,count
queues_old_msgs[k]=count
except:
print "failed to retrieve all messages for queue:",k
for k,v in queues_old_msgs.iteritems():
print "old messages in queue,count:",k,v
</code></pre>
<br />
<br />jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-73802440866647633412014-03-03T23:03:00.000-08:002019-09-15T01:28:22.772-07:00Avro - a simple exampleWhen moving data from one place to another or just storing it, there are loads of options from plain text to specialized, binary formats. Somewhere in the middle are XML, JSON, ProtocolBuffers, Thrift and a newer entry Avro. <a href="http://avro.apache.org/">Avro </a>differs a little from some of these as it is in a binary format like protobufs and Thrift, but unlike these two, it also stores the schema with the file. It is easy to use being very similar to protobufs and Thrift or even XSD derived classes. <br />
<br />
Follow the detail below (or the <a href="http://avro.apache.org/docs/1.7.6/gettingstartedjava.html">simple tutorial on the Avro pages</a>): <br />
<br />
Download or use dependency management (maven/gradle/etc) to get: avro-1.7.6.jar and avro-tools-1.7.6.jar and Jackson JSON library - specifically, core-asl and mapper-asl jars (those are 1.9.x jar names) or core for v2.x of Jackson. Make sure they're on the build path.<br />
<br />
Create a schema (as in example.avsc): <br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> {"namespace": "example.avro",
"type": "record", "name": "MyExample",
"fields": [
{"name": "title_of_doc", "type": "string"},
{"name": "author_name", "type": ["string", "null"]},
{"name": "number_pages", "type": ["int", "null"]}
]
}
</code></pre>
... and run the avro command line tool to generate the class:<br />
<span style="color: white;"><span style="background-color: black;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">cd .../workspace/avro-example/</span></span></span></span><br />
<span style="color: white;"><span style="background-color: black;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">java -jar /path/to/avro-tools-1.7.6.jar compile schema example.avs<span style="background-color: black;">c</span></span></span></span><span style="background-color: black;"> .</span></span><br />
which will create a myExample.java file in example/avro folder<br />
<br />
Move the example/avro folder to be under src or move the newly created file to be under src/example.avro package or add the new file to the build path.<br />
<br />
Put the schema to use by pulling it in as a class and creating a few instances - note the different constructors. Then open a writer and filewriter to write out the data, then open a reader and filereader to pull it back in - that should cover the basics! Note that the reader and writer and their corresponding filereader and filewriter can have differing schemas - in case you have versioning and want to open a file with one schema, but operate on the data with another.<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> package example.avro;
import java.io.File;
import java.io.IOException;
import org.apache.avro.file.DataFileReader;
import org.apache.avro.file.DataFileWriter;
import org.apache.avro.io.DatumReader;
import org.apache.avro.io.DatumWriter;
import org.apache.avro.specific.SpecificDatumReader;
import org.apache.avro.specific.SpecificDatumWriter;
public class AvroEx {
public static void main(String args[]){
MyExample exmplDoc = new MyExample(); //basic constructor, class from the record name in avsc file
exmplDoc.setTitleOfDoc("Testing for fun"); //notice it replaced title_of_doc with TitleOfDoc
exmplDoc.setNumberPages(123);
MyExample exmplDoc2 = new MyExample("Growing Green Software",322,"Mr Green"); //alt constructor
MyExample exmplDoc3 = MyExample.newBuilder().setTitleOfDoc("Forget Testing") //using builder requires setting
.setAuthorName("Miss Read").setNumberPages(null) //all fields even if null
.build();
//Write out an AVRO file
File file = new File("Example-out-in.avro");
DatumWriter<MyExample> userDatumW = new SpecificDatumWriter<MyExample>(MyExample.class); //serialize in memory
DataFileWriter<MyExample> dataFW = new DataFileWriter<MyExample>(userDatumW); //allow difference schema if necessary
try {
dataFW.create(exmplDoc.getSchema(), file);//write schema and records to file
dataFW.append(exmplDoc);
dataFW.append(exmplDoc2);
dataFW.append(exmplDoc3);
dataFW.close();
} catch (IOException e) {
e.printStackTrace();
}
//Read in AVRO data
DatumReader<MyExample> userDR = new SpecificDatumReader<MyExample>(MyExample.class);
try {
DataFileReader<MyExample> dataFR = new DataFileReader<MyExample>(file,userDR); //schema option again
MyExample userReadIn = null;
while (dataFR.hasNext()){
userReadIn = dataFR.next(userReadIn);
System.out.println(userReadIn);
}
dataFR.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
</code></pre>
<br />
Run the AvroEx.java file as an application. It will create the avro file for writing and reading and print out the data that was written out and read back in.<br />
<br />
<br />
<br />jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-30140625761938880052014-02-22T23:18:00.000-08:002019-03-23T05:07:57.635-07:00Akka in 100 lines - Actors and Message PassingHere is a quick program in Akka (2.2 in case it matters). The program is kept short at 100 lines to make it easier to understand what is going on which is also why there are many println statements and a few unused variables. It's a starting point for getting something bigger running!<br />
<br />
Akka uses the actor model and message passing to run a distributed processing system. This model has been a standard approach in high performance computing for decades (<a href="https://computing.llnl.gov/tutorials/mpi/#What">see MPI for examples that started in the 1980s</a> or the similar, although heavier, idea of fork() from unix in the 1970s). Akka brings an excellent model of this to Java in a more native fashion (there are <a href="http://en.wikipedia.org/wiki/Message_Passing_Interface">MPI-Java bindings</a>). Message passing is a different approach to concurrent processing than multi-threading as threads have things such as shared memory and deadlocks for those shared objects and less easier distributed computing (multi-threading is often within one jvm) where message passing are independent workers with independent memory (fork is somewhat different as it cloned the parent's memory as well which workers don't usually do). <br />
<br />
This program starts 12 workers and collects their results for final display. You may get messages about dead letters (and the number may vary between runs) - see<a href="http://doc.akka.io/docs/akka/snapshot/java/logging.html#Logging_of_Dead_Letters"> the Akka docs on this</a> (snapshot; <a href="http://doc.akka.io/docs/akka/2.2.1/java/logging.html#Logging_of_Dead_Letters">v2.2.1 is here</a>) to understand, but the code is still working fine in this case. <br />
<br />
Remember to add a few jars to your library/classpath: <span style="font-family: "courier new" , "courier" , monospace;">scala-library.jar, akka-actor_2.10-2.2.0.jar</span>, and <span style="font-family: "courier new" , "courier" , monospace;">config-1.0.2.jar</span> which are all taken from the akka-2.2.0 distribution in /lib.<br />
<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> package tutorial;
import akka.actor.*;
import akka.japi.*;
import akka.routing.RoundRobinRouter;
public class Example {
public static void main(String args[]) {
System.out.println("Starting main ...");
Example xam = new Example();
xam.callActorsToCalculate();
// Main will finish before all of the threads finish
System.out.println("Finishing main ..."); //note when this is displayed in output
}
public void callActorsToCalculate() {
// create the basic Akka system:
ActorSystem xamSys = ActorSystem.create("example-system"); // no "_" allowed
// set up actor that will control the worker actors via messages; props first, then actor
Props pMaster = Props.create(MyMasterActor.class, "master_hello", 12);
final ActorRef masterRef = xamSys.actorOf(pMaster);
System.out.println("...(properties for master) and now MasterActor ready");
// start the master off with empty msg; should use ActorRef.noSender() leaving as is for clarity
masterRef.tell(new StartCalculationMessage(), masterRef.noSender());
System.out.println("just sent tell 'StartCalculationMessage' to Master...");
}
public static class MyMasterActor extends UntypedActor {
int numberOfWorkers;
int numberOfResults = 0;
int resultSum = 0;
private final ActorRef workActionsRouter;
public MyMasterActor(String name, int numOfSubWorkers) { // normal constructor
numberOfWorkers = numOfSubWorkers;
System.out.println("constructing "+ numberOfWorkers + " workers with str:"+name);
// create the router to send 'call to action' msg to workers
workActionsRouter = this.getContext()
.actorOf(new Props(MyActorWorker.class).withRouter(new RoundRobinRouter(
numberOfWorkers)), "workerRouter"); //use Props.create as above
System.out.println("workerRouter ready");
}
@Override
public void onReceive(Object message) throws Exception { // required method
if (message instanceof StartCalculationMessage) {
// the starting message has been received - kick off the actors
// to do the work
System.out.println("starting workers");
for (int numWork = 0; numWork < numberOfWorkers; numWork++) {
workActionsRouter.tell(new WorkStartMsg(), getSelf());
}
} else if (message instanceof WorkerResult) {
WorkerResult workerResult = (WorkerResult) message;
System.out.println("master received message back from worker");
numberOfResults++;
resultSum += workerResult.result*numberOfResults;
if (numberOfResults == numberOfWorkers) { // have all results come back?
System.out.println("A message from MasterActor:"
+ (numberOfResults + 100)+" sum="+resultSum);//could have called a FinalActor with Final msg
getContext().stop(getSelf()); //stopping the master (and thus its children)
getContext().system().shutdown(); // stop the system - seems like it
//should be in the method that started it, but that method has exited already
}
} else {
System.out.println("Unhandled message in master");
unhandled(message);
}
}
}
static class StartCalculationMessage {
} // empty class to use for messaging the start of work
static class WorkStartMsg {
} // message sent to workers, in this case to start off the work
static class WorkerResult { // data class to store the result info
private final int result;
WorkerResult(int value) { result = value; }
public int getResult() { return result; }
}
static class MyActorWorker extends UntypedActor {
@Override
public void onReceive(Object message) throws Exception {
if (message instanceof WorkStartMsg) {
WorkStartMsg workMsg = (WorkStartMsg) message; //unused, but could be to set starting point, e.g.
int result = 10; // set result to some value
getSender().tell(new WorkerResult(result), getSelf());
System.out.println("worker:start msg:"+this.toString()+"; & msg'd master");
} else {
System.out.println("Unhandled message in worker");
unhandled(message);
}
}
}
}
</code></pre>
<br />
For other, quick introductions, look here:
<a href="http://doc.akka.io/docs/akka/2.0.2/intro/getting-started-first-java.html">http://doc.akka.io/docs/akka/2.0.2/intro/getting-started-first-java.html</a> and
<a href="http://java.dzone.com/articles/your-first-message-discovering">http://java.dzone.com/articles/your-first-message-discovering</a><br />
<br />
Details on some of the specifics around actors can be found: <a href="http://doc.akka.io/docs/akka/snapshot/java/untyped-actors.html">http://doc.akka.io/docs/akka/snapshot/java/untyped-actors.html</a><br />
<br />
A more detailed introduction to actors is here: <a href="http://www.javaworld.com/article/2078775/scripting-jvm-languages/open-source-java-projects-akka.html">http://www.javaworld.com/article/2078775/scripting-jvm-languages/open-source-java-projects-akka.html</a><br />
<br />
A simple example for load testing: <a href="http://www.javacodegeeks.com/2012/05/processing-10-million-messages-with.html">http://www.javacodegeeks.com/2012/05/processing-10-million-messages-with.html</a><br />
<br />jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-30977511840327618252013-10-01T15:53:00.000-07:002014-03-10T23:26:09.479-07:00Persistence Adaptors and ActiveMQ OptionsWhere do you find ActiveMQ options that aren't listed on the ActiveMQ site pages? And how do you configure the newer, more pluggable persistence and lock adapters?<br />
<br />
Recently, we've had problems around ActiveMQ again - less in terms of the brokers than in the clients having ever increasing numbers of connections causing the broker to eventually crash. While we try to understand the issue with the clients (unlikely due to ActiveMQ), we tried to reconfigure the ActiveMQ brokers to cope with the load. One thing mentioned previously was turning off Network of Brokers - it fails too easily and ends up in a split brain. Restarting a broker fixes it, but can kick off the clients - consumers and producers - into increasing connections. In other words, we were trapped - increasing connections causing ActiveMQ problems causing increasing connections causing ...<br />
<br />
While we were experimenting, we went back to a SQL persistence layer rather than KahaDB. For pseudo random testing, it was about 60-70% as performance as KahaDB, but in real work loads, it seemed noticeably slower. The consumers and producers seemed to think so as well as the spiraling connections was much worse with SQL backed storage, presumably due to slower performance causing more connection issues causing slower performance causing ... you get the picture.<br />
<br />
We're tempted by a solution that is based completely NFS shared storage, but some colleagues are nervous about KahaDB stale file issues over NFS and shared between brokers. If we have KahaDB files that won't clear and are shared between two brokers, it seems even more problematic than our current set up.<br />
<br />
A couple of ideas: ActiveMQ supports mKahaDB which allows you to have different KahaDB files for different queues or topics (with "catch all" defaults available). That way the slow and fast consumers can be separated from each other and the file size can be controlled better. It might also help with stale file issues. See this page for more: http://activemq.apache.org/kahadb.html<br />
Another idea is to switch to LevelDB which should be faster than KahaDB according to the ActiveMQ site and also might not suffer from the stale file problems. Or maybe it does.<br />
Yet another option is to use the ActiveMQ pluggable storage lockers http://activemq.apache.org/pluggable-storage-lockers.html and set the storage to be a local directory for each broker and the lock file can be a shared directory.<span style="color: #990000;">**</span> Ok, this option isn't great because messages could be stranded, especially if you have some slow consumers, but for us, we might prefer a little manual work after a failover instead of risking down time - well some of the other guys.<br />
<br />
Regardless of approach, the pluggable storage has the ability to separate locks from data if needed and to set some options.<span style="color: #990000;">**</span> Look at this for a quick example of<br />
<persistenceAdapter><br />
<kahaDB directory="activemq-data"><br />
<locker> <br />
<shared-file-locker lockAcquireSleepInterval="100000"/><br />
</locker><br />
</kahaDB><br />
</persistenceAdapter><br />
For setting the lock directory, use:<br />
<shared-file-locker directory="activemq-lock-directory"/><br />
or similar. A few of the options for the pluggable storage and lockers is on the main ActiveMQ site, but there are more that aren't.<br />
<br />
Finding all the ActiveMQ XML configuration options, including the storage locking ones, is easiest by looking directly at the ActiveMQ XML. Here's the link to the kahaDB store:<br />
activemq.apache.org/schema/core/activemq-core-5.8.0-schema.html#kahaDB<br />
and to the shared-file-locker which is a possible element of that store:<br />
http://activemq.apache.org/schema/core/activemq-core-5.8.0-schema.html#shared-file-locker<br />
Be aware of version numbers in the two links above (both are for 5.8.0).<br />
The ActiveMQ guys are in the process of finishing the separation of storage vs locks, but not all made it into 5.8 - mixing kaha with SQL lease locks might have to wait until 5.9 (and 5.9 is out with the SQL lease locks):<br />
https://issues.apache.org/jira/browse/AMQ-4365<br />
<br />
*** Update - a little delayed in mentioning this, but ActiveMQ 5.9 appears to have all that is needed to <a href="http://activemq.apache.org/pluggable-storage-lockers.html">use shared locking with individual kahadb stores</a> - we'll test that and report back.<br />
The options in the <statements/> section are <a href="https://fisheye6.atlassian.com/browse/activemq/trunk/activemq-jdbc-store/src/main/java/org/apache/activemq/store/jdbc/Statements.java?hb=true">available by looking at the code</a> which also has useful pieces like the SQL create statements. Hopefully, we can drop the work below now!<br />
<br />
<span style="color: #990000;">**Ok, one problem - the XML supports the shared-file-locker
directoy="..." syntax, but the code does NOT do anything with it! After
trying this, we realized the lock file was still being put in the same
location as the data files. Reviewing the ActiveMQ code (search for
sharedfilelocker.java) made it clear that it hasn't been finished yet.
So, how to use a feature like this: linux based file locking - if you
set a script that either locks or changes ownership of the 'lock' file
then you'll control ActiveMQ start up. Detecting and setting the lock
requires a little work with NFS, flock, or perhaps something like python
- actually just changing the ownership is easier. Since you're trying
to detect if the other ActiveMQ is running, looking for a lock is nice,
but you could just curl one of the standard URLs on the other broker to
see if it is running - not foolproof, but perhaps workable with the
right supplemental checks.</span><br />
<br />
One option to having a shared lock is to try the DB shared locker, but in the spirit of avoiding the DB (and it would be fine to use it for a lock!), here's a little script that flags the lock by leaving a file on the NFS mount (msg_dir). It's set to look at /proc/kmsg as a test, but change it to the activemq/lock file instead.<br />
<span style="color: #990000;"><span style="color: black;"><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br />#check if file is locked or at leasted opened and indicate with another file<br /># using this as flock across NFS didn't initially work for me<br />fn=/proc/kmsg<br />#fn=$activemqdir/lock<br />hn=`hostname`<br />msg_dir=/tmp<br />fn=$msg_dir/file_is_locked_on_$hn<br />standby_server="no"<br />amq_lock_file=/tmp/activemq_home_lock<br /><br />#exit value should be 0 for an opened file and fopened should have a process id<br /># else file shouldn't be opened and therefore should be in use by activemq<br /><br />if [ -e $fn ]<br /> then<br /> fopened=`lsof -wt $fn`<br /> exit_value=$?<br /> else<br /> echo "no file to check!"<br /> fopened=""<br /> exit_value=1<br />fi<br /><br /><br />if [ "$fopened" != "" -a $exit_value -eq 0 ]<br /> then<br /> echo "file is locked"<br /> echo $fopened > $msg_dir/file_is_locked_on_$hn<br /> rm -f $msg_dir/file_NOT_or_unknown<br /> if [ "$standby_server" = "yes" ]<br /> then<br /> sleep 5 #make the standby server sleep waiting to see if race for lock with another server<br /> remote_lock=`ls -1 $msg_dir | grep locked | grep -v $hn | wc -l`<br /> if [ $remote_lock -gt 0 ]<br /> then<br /> touch $msg_dir/file_NOT_or_unknown<br /> rm -f $msg_dir/file_is_locked_on_$hn<br /> else<br /> echo "still not locked remotely so setting for local startup"<br /> fi<br /> fi<br /> else<br /> echo "not locked or unknown"<br /> touch $msg_dir/file_NOT_or_unknown<br /> rm -f $msg_dir/file_is_locked_on_$hn<br />fi<br /><br /># Have left a marker that one instance is up or not, now use that to control activemq<br /><br />remote_lock=`ls -1 $msg_dir | grep locked | grep -v $hn | wc -l`<br />local_lock=`ls -1 $msg_dir | grep locked | grep $hn | wc -l`<br />echo "remote lock: $remote_lock; locally locked: $local_lock"<br />if [ $remote_lock -gt 0 ]<br /> then<br /> chown root.root $amq_lock_file #prevent activemq from locking and starting<br />elif [ $local_lock -eq 1 ]<br /> chown activemq.activemq $amq_lock_file #allow activemq to lock and start<br /> # could just start activemq at this point<br /> then<br />fi</span></span></span><br /> </span>jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com4tag:blogger.com,1999:blog-4804459376450494896.post-46001066303228971462013-09-16T13:47:00.000-07:002019-03-10T11:21:32.322-07:00More ActiveMQ admin URLsIn a previous post, we looked at querying ActiveMQ to get some details about queues. These details are very useful to see a detailed view of the system. Here are two more ways of looking into ActiveMQ - one more superficially and the other in detail.<br />
<br />
Given that we run ActiveMQ in a production environment, we need to know that all of our applications are consuming messages and events from ActiveMQ as we'd expect. We can monitor the apps themselves for their throughput, but that depends on how many messages we have queued up for the app. Previously, we saw how to use <a href="http://working-with-activemq.blogspot.co.uk/2012/09/admin-tools-for-activemq.html">activemq-admin query</a> to find the <a href="http://working-with-activemq.blogspot.co.uk/2013/04/performance-issues.html">number of messages enqueued, dequeued, dispatched</a>, etc. However there are a couple of other, perhaps easier, ways to get some basic information about the number of messages in a queue. Using the web UI, you can get the number of messages programmatically just as you would browse yourself:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">localhost:8161/admin/browse.jsp?JMSDestination=test123</span><br />
<br />
Below is a way to get an XML list of the queue message ids. It is still under /demo/ in 5.8, but you'll need to enable demo in the jetty.xml file (look at jetty-demo.xml for the demo section): <br />
<span style="font-family: "courier new" , "courier" , monospace;">localhost:8161/demo/queueBrowse/my_queue_name </span><br />
This is a quick and easy way to get queue lengths and allows you to 'diff' against a previous check to see if the messages have moved much at all such as:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">curl localhost:8161/demo/queueBrowse/my_queue_name > /tmp/id_list.now</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">sleep 30 # sleeps 30 seconds</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">mv /tmp/id_list.now /tmp/id_list.30s_old </span><br />
<span style="font-family: "courier new" , "courier" , monospace;">curl localhost:8161/demo/queueBrowse/my_queue_name > /tmp/id_list.now</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">diff /tmp/id_list.30s_old /tmp/id_list.now #</span> you can grep for newer entries (grep ^>) and count them<br />
<br />
All easy stuff. In 5.9 and above, the urls have moved a little and you'll still need to enable as desired.<br />
Viewing and consuming messages (and unsubscribing):<br />
<span style="font-family: "courier new" , "courier" , monospace;">localhost:8161/demo/message/myTestQueue?type=Queue</span> (5.8 and below)<br />
<span style="font-family: "courier new" , "courier" , monospace;">localhost:8161/api/message/myTestQueue?type=Queue</span> (5.9 and above)<br />
Viewing xml message lists:<br />
<span style="font-family: "courier new" , "courier" , monospace;">localhost:8161/demo/queueBrowse/myTestQueue</span>(5.8 and below)<br />
<span style="font-family: "courier new" , "courier" , monospace;">localhost:8161/admin/queueBrowse/myTestQueue</span>(5.9 and above)<br />
<br />
Don't forget that you can add the value <code class="java comments">&clientId=myConsumerId123 </code>to the url to keep session state easily, as in:<br />
<span style="font-family: "courier new" , "courier" , monospace;">localhost:8161/demo/message/myTestQueue?type=Queue&clientId=myNewConsumer</span> (5.8 and below)<br />
and then unsubscribe that client when done (or it will time out eventually):<br />
<span style="font-family: "courier new" , "courier" , monospace;">localhost:8161/demo/message/myTestQueue?clientId=myNewConsumer&action=unsubscribe</span><code class="java comments"></code> (5.8 and below) <br />
<br />
<b>JMX information</b><br />
ActiveMQ 5.8 added a nice feature in the form of <a href="http://www.jolokia.org/">Jolokia </a>which gives easier access to JMX information. With ActiveMQ 5.8+, check out URLs like these:<br />
<span style="font-family: "courier new" , "courier" , monospace;">http://localhost:8161/api/jolokia <span style="font-family: "times" , "times new roman" , serif;">(really only good for the time stamp) </span></span><br />
<span style="font-family: "courier new" , "courier" , monospace;">http://localhost:8161/api/jolokia/read/org.apache.activemq:type=Broker,brokerName=localhost</span><br />
which shows you details such as the consumer count. <br />
There's another example at the bottom of <a href="http://activemq.apache.org/rest.html">this page</a> as well. <br />
<br />
<br />jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-46741530598778271422013-08-26T10:09:00.000-07:002019-03-10T10:20:36.701-07:00Using rrdtoolRRDTool and Round Robin Databases<br />
<br />
A round-robin
database (rrd) is basically a fixed size database that will overwrite
the oldest data when all the other spaces have been used. This behavior
is common with caches using the least recently used approach, for
example. However, it seems a little odd for databases - after all, you
want to store things and that is why you put it in a database, right?
Well, maybe not entirely right for entirely all data - imagine you
wanted to store time series data where old values were aggregated (less
detailed information) together and stored elsewhere or where data one
year old just doesn't matter anymore.<br />
<br />
RRDtool is a
round robin database and a suite of tools all in one (graphite is a
somewhat nicer alternative). You can store data in RRDtool and retrieve
the data or better yet, retrieve plots of the data.<br />
<br />
(Update from 2019: <a href="https://db-engines.com/en/system/Graphite%3BPrometheus%3BRRDtool">DB-Engines</a> ranks RRDtool still comparable to Graphite and Prometheus although Prometheus has been rising very quickly for the last few years. This is likely due to what RRDtool did to open up this field years ago.)<br />
<br />
Here are some simple commands for using RRDtool: <br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="color: #eeeeee;"><span style="background-color: black;">sudo yum install rrdtool #install or sudo apt-get install rrdtool </span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="color: #eeeeee;"><span style="background-color: black;"><br /></span></span></span></span>
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="color: #eeeeee;"><span style="background-color: black;">man rrdtool #if you want to read man pages </span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="color: #eeeeee;"><span style="background-color: black;">man rrdcreate #more details on creating an rrd file</span></span></span></span><br />
<br />
Create
an example rrd for storing counts of updates versus time - it's made up
metric here, but could be a count of hits on a webpage or number of
packets on a network interface:<br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"> rrdtool create counts.rrd --start 1377510000 \ #set start time if needed</span></span></span></span><br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"> -s 300 DS:count_updt:COUNTER:600:U:U RRA:AVERAGE:0.5:1:12 RRA:AVERAGE:0.5:3:10 <br />#step
300s, COUNTER type, assume null after 600s, Unknown for min, Unknown
for max, RRA - round robin aggregation patter: AVERAGE values to
consolidate .5 (1/2) values needed to consolidate, every 1 step (maybe
silly), 12 times; then another set with .5, 3 steps consolidated, 10
times (step = 5m, 3 steps = 15m, 10 points = 150m)</span></span></span></span><br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><br /></span></span></span></span>
If you read the manual pages, then you'll see CF frequently. CF is the
'consolidation function' or how RRDtool consolidates data for storage or
display in a plot. Above, the CF is AVERAGE, there are others and more
sophisticated approaches like Holt-Winters.<br />
<br />
Load some
data using the Unix timestamp (date +%s). Below, we're loading a number
of points starting at 1377510000 which works with the rrd created above.
You may need to update this to the current time if you change things.<br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"> rrdtool update counts.rrd 1377510000:1234 1377510300:1230 1377510600:1200<br /> rrdtool update counts.rrd 1377510900:1363 1377511200:963 1377511500:1275<br /> rrdtool update counts.rrd 1377511800:1083 1377512100:999 1377512400:1099<br /> rrdtool update counts.rrd 1377512700:1500 1377513000:1810 1377513300:840 </span></span></span></span><br />
<br />
Pull the data back to double check - first pull it, then over a time range, then a dump:<br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"> </span></span></span></span><span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">rrdtool fetch counts.rrd AVERAGE</span></span></span></span><br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"> rrdtool fetch counts.rrd AVERAGE --start 1377510000 --end 1377513000<br /> rrdtool dump counts.rrd</span></span></span></span><br />
<br />
Create an image from the data: <br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"> rrdtool
graph count_updt.png --start 1377510000 --end 1377513000
DEF:count_updt=counts.rrd:count_updt:AVERAGE LINE2:count_updt#FF0000 </span></span></span></span><br />
<br />
DEF:virtual_name=rrd_filename:data-source-name:CF
defines a virtual_name for the rrd_filename followed by a
data-source-name and consolidation function, but here we've only used
one data source (graphite is a little easier for things like this, but
once you have the format, rrdtool is fine). LINE2:... means use a line,
weight 2, data-source-name and HTML color code.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYfMAM5_i5mLAtB7d47xdFeiLJ_4pVmnDbb1gqiHW5gvNUFGR3_Zsqij0Jlrc07xZXRNtyOYQhnggQ56lCW38YHGaLHEbDTvqS5WmMdAM1l_Ue7qZ1ba44Tw4DXwRy_V4Cm0KLbj8yeug/s1600/count_updt.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="141" data-original-width="481" height="93" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYfMAM5_i5mLAtB7d47xdFeiLJ_4pVmnDbb1gqiHW5gvNUFGR3_Zsqij0Jlrc07xZXRNtyOYQhnggQ56lCW38YHGaLHEbDTvqS5WmMdAM1l_Ue7qZ1ba44Tw4DXwRy_V4Cm0KLbj8yeug/s320/count_updt.png" width="320" /></a></div>
<br />
<br />
<br />
Check the source docs (<a href="http://oss.oetiker.ch/rrdtool/doc/rrdgraph.en.html">here</a> and <a href="http://oss.oetiker.ch/rrdtool/doc/rrdgraph_data.en.html">here</a>) for more details information. Also, read the man pages:<br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"> man rrdgraph_data</span></span></span></span><br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"> man rrdgraph_graph</span></span></span></span><br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"> man rrdgraph_examples</span></span></span></span><br />
<br />
Let's
create a little more involved one without the rigid time settings. The
type has been switched from COUNTER to GAUGE (counter expects increasing
values while gauge can vary up and down):<br />
<br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"> rrdtool create counts1.rrd --start now-7200 DS:count_updt:GAUGE:600:U:U RRA:AVERAGE:0.5:1:12 RRA:AVERAGE:0.5:3:10</span></span></span></span><br />
Fill with some data - in this case increasing values each time:<br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"> i=0; d=`date +%s`<br /> while
[ $i -lt 24 ] ; do echo $i; let i=$i+1; let t1=d-7200+300*$i;
c=$(($i*500)); echo $t1 $c ; rrdtool update counts1.rrd $t1:$c ; done</span></span></span></span><br />
Have a look at the data, if you want:<br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">rrdtool dump counts1.rrd</span></span></span></span><br />
Create a plot:<br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"> rrdtool graph count_secd.png --start now-7200 DEF:count_updt=counts1.rrd:count_updt:AVERAGE AREA:count_updt#FF0000</span></span></span></span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9OoziGS8zkPlugBn_Bg5atNR9x7R7GYy2ZqBzzoMsLUx9xVIt1kQwsHQmWrANQvnZIakYiCmnpLg6_dwnZVg4dwmJpzdBjq2t9kPHdtzsDboUvohMvsB1a-piM5dDVhH3mR8on8cJjMs/s1600/count_secd.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="141" data-original-width="481" height="93" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9OoziGS8zkPlugBn_Bg5atNR9x7R7GYy2ZqBzzoMsLUx9xVIt1kQwsHQmWrANQvnZIakYiCmnpLg6_dwnZVg4dwmJpzdBjq2t9kPHdtzsDboUvohMvsB1a-piM5dDVhH3mR8on8cJjMs/s320/count_secd.png" width="320" /></a></div>
<br />
Now, let's create and fill an rrd like the above, but with varying data:<br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">d=`date +%s`</span></span></span></span><br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">rrdtool create noise.rrd --start=now-7200 DS:noise_meas:GAUGE:600:U:U RRA:AVERAGE:0.5:1:12 RRA:AVERAGE:0.5:3:10</span></span></span></span><br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> i=1</span></span></span></span><br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> rrdtool dump noise.rrd</span></span></span></span><br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> let t=$d-7200</span></span></span></span><br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> t=$(($d-7200))</span></span></span></span><br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> while [ $i -lt 24 ] ; do echo $i; let i=$i+1; let t1=t+300*$i; v=`echo $RANDOM`; rrdtool update noise.rrd $t1:$v ; done</span></span></span></span><br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> rrdtool
graph noise.png DEF:noise_meas=noise.rrd:noise_meas:AVERAGE
LINE2:noise_meas#00FF00:"example_line\l" -t "Sample Graph" -v "values"
-w 500 -h 200 -c BACK#AAAAAA -c GRID#333333</span></span></span></span><br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"> rrdtool
graph noise.png --start now-7200
DEF:noise_meas=noise.rrd:noise_meas:AVERAGE
AREA:noise_meas#00FF00:"example_line\l" -t "Sample Graph" -v "values" -w
500 -h 200 -c BACK#AAAAAA -c GRID#333333</span></span></span></span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFdClOF7rRVs7okgJ11s34cSsWDaxymJtlU4ssTxQu4xOCTSHK-Oq-c40qnQbQwITYIlxAygZhJyaM1ZEsJCvW3WGizy2QUSMYbxgdHK0yS4_7BpQJ3NwJnJ30UZwd3QXW_ituYI98Vo0/s1600/noise.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="268" data-original-width="597" height="143" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFdClOF7rRVs7okgJ11s34cSsWDaxymJtlU4ssTxQu4xOCTSHK-Oq-c40qnQbQwITYIlxAygZhJyaM1ZEsJCvW3WGizy2QUSMYbxgdHK0yS4_7BpQJ3NwJnJ30UZwd3QXW_ituYI98Vo0/s320/noise.png" width="320" /></a></div>
Now, let's create a plot with the data from the noise and counts1 data:<br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;"> rrdtool
graph noise_count.png --start now-7200
DEF:noise_meas=noise.rrd:noise_meas:AVERAGE
DEF:count_updt=counts1.rrd:count_updt:AVERAGE
AREA:noise_meas#00FF00:"example_line\l"
LINE2:count_updt#FF0000:"count\l" -t "Sample Graph" -v "values" -w 500
-h 200 -c BACK#AAAAAA -c GRID#333333</span></span></span></span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRXWBjCIzQEstxeZwpgmuLODjdgZj0e1rXFpaJJ_07fMwtoMLBC06X5JM9Uq49Gzr5p6YyCNXQ-px_mJEawkvgG4q-_fFoocia5zmJb0EtXWsDknH8ZsTzoH_EQtPEsM9AygNCJkPlI28/s1600/noise_count.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="282" data-original-width="597" height="151" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRXWBjCIzQEstxeZwpgmuLODjdgZj0e1rXFpaJJ_07fMwtoMLBC06X5JM9Uq49Gzr5p6YyCNXQ-px_mJEawkvgG4q-_fFoocia5zmJb0EtXWsDknH8ZsTzoH_EQtPEsM9AygNCJkPlI28/s320/noise_count.png" width="320" /></a></div>
<u><b>Using with Python </b></u><br />
To
make RRDtool more useful, it's good to link it with a bit of code to
insert data as desired. Here, we're using python-rrdtool is the package
(install python-rrdtool) with info <a href="https://oss.oetiker.ch/rrdtool/prog/rrdpython.en.html">here</a> and <a href="https://supportex.net/2011/09/rrd-python/">here</a>. There are other python pages and packages for other libraries. <br />
<br />
Using python with rrdtool will require a package install like this: yum install python-rrdtool<br />
<br />
Add some python (fairly straight forward, but details depend on the package):<br />
#quick set of python commands for putting rrd data into rrdtool:<br />
<span style="background-color: #cfe2f3;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">from rrdtool import update as rrdtool_update</span></span></span><br />
<span style="background-color: #cfe2f3;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">value = "30"</span></span></span><br />
<span style="background-color: #cfe2f3;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"><span style="color: #eeeeee;"><span style="color: black;">result = rrdtool_update('counts.rrd', '1552217030:30')</span> </span></span></span></span><br />
<span style="background-color: #cfe2f3;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"># this was the format for rrdtool-python:</span></span></span><br />
<span style="background-color: #cfe2f3;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">#result
= rrdtool_update('test.rrd','N:%s',%(value))
#N means now, otherwise could specify a specific unix timestamp and
could send two values if both created in test.rrd
'N:%s:%s',%(value1,value2) </span></span></span><br />
<br />
Combine
this with some web calls (see the python page on this blog) and you'll
be storing time series data quickly! Before you write your own simple
receiver, though, have a look at <a href="https://oss.oetiker.ch/rrdtool/doc/rrdcached.en.html">rrdcached</a> which handles receiving metricsjmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-8934343297942441412013-08-17T15:36:00.000-07:002019-03-11T15:36:34.844-07:00Using Graphite - metrics dataGraphite is a tool for time series data storage and graphing. The main page is here: <a href="https://graphite.readthedocs.io/en/latest/">Graphite</a>.<br />
<br />
Graphite
(and its Carbon receiver and Whisper data store) stores a metric value
against a specific time stamp for the metric that you want to track. In
other words, if you want to know the number of requests per minute that
a web server is receiving, you'd send the data to your graphite set up
with a command like this (from a Unix/Linux/max command line):<br />
<br />
<pre><span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">echo "webserver1.requests_per_minute 201 1376748060" | nc graphite_svr.mycomp.com 2003</span></span></span></span></pre>
<b style="-webkit-text-stroke-width: 0px; background-color: white; color: black; font-family: 'Times New Roman'; font-size: medium; font-style: normal; font-variant: normal; letter-spacing: normal; line-height: normal; orphans: auto; text-align: -webkit-center; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;"><br /></b>
Where webserver1.requests_per_minute is the metric to be saved. 201 is
the value of the metric and 1376748060 is the time in seconds since Jan
1, 1970. This simple set of values - metric name, metric value, metric
timestamp in seconds is all that's needed by Graphite.<br />
In the
example above, this information is then piped into the netcat command to
the graphite server at port 2003, the default port for Graphite. There
are other ways to get information into Graphite - python's pickle
format, for example (there's also apparently AMQP support).<br />
<br />
Graphite
will store this data into a whisper directory as set up in the graphite
installation. Whisper is the database Graphite uses to store data. It
is a round-robin database very similar to rrdtool's storage (rrd meaning
round robin database) where Graphite started off; however, limitations
in the version of rrd then led to the creation of whisper.<br />
<br />
In
the Graphite/Whisper storage area, the metric data will be stored in a
hierarchy based on the metric name given. In the example above, the
metric name is webserver1.requests_per_minute which will lead to a
directory called webserver1 in which there will be a file called
requests_per_minute.wsp. Graphite uses the "." as a delimiter to create
the hierarchy. <br />
<br />
To view the data, use a web browser to
go to the graphite front end (for example, graphite_svr.mycomp.com)
where you can browse the metrics that Graphite is storing and create
graphs and dashboards of graphs for viewing. Individual charts can be
viewed by creating the right URL as in:<br />
<br />
<a href="https://mail.thehut.net/owa/redir.aspx?C=A1psoQ2XsUiZ6bPPUbZ_AuSWuObkbtAIfXoMBsAsrTyfimla-T3FkkHMpwW0f8ncfhldgTrXkEg.&URL=http%3a%2f%2fgraphite.ss.thehutgroup.local%2frender%3fwidth%3d400%26from%3d-24hours%26until%3d-%26height%3d250%26target%3dgblmsg005.ordermanager.orderMonitorSecPerOrder%26target%3dsherlock.AllocationResource.allocate.mean%26_uniq%3d0.1418312011678139" target="_blank">http://graphite_svr.mycomp.com/render?width=400&from=-24hours&until=-&height=250&target=webserver1.requests_per_minute&target=webserver2.requests_per_minute</a><br />
<br />
This
URL will cause Graphite to draw a graph of the requests_per_minute from
webserver1 and webserver2 for the last 24 hours until now and with a
chart size of 400x250 pixels.<br />
<br />
To see the raw data, add
"&format=raw" to the end of a request; it will print the data per
time slot, but won't show the time stamp. To see the time stamp and the
values, you'll need to use some Whisper commands. To view json data, add "&format=json" instead.<br />
<br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">whisper-fetch.py requests_per_minute.wsp</span></span></span></span> will show the data with the timestamps. <br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">whisper-info.py requests_per_minute.wsp</span></span></span></span> will show basic information about the wsp file such as the expected time intervals<br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">whisper-resize.py
requests_per_minute.wsp 5m:1y 30m:3y</span></span></span></span> will resize the whisper data file
to store data every 5 minutes for a year and then start aggregating
values to 30 minutes for 3 years.<br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">whisper-update.py
requests_per_minute.wsp 1376748060:199</span></span></span></span> will overwrite the currently
stored value at time 1376748060 (201) with the new value (199). I've
had trouble getting this command to work, but have had success
resubmitting the information via the netcat command as above.<br />
whisper-dump.py will show a mix of whisper-info.py and whisper-fetch.py data including unfilled slots.<br />
whisper-create.py
to create a new metric file - this isn't needed as sending the data to
Graphite will cause it to create the file with defaults matching the
metric.<br />
<br />
Whisper files are created with default values set in the <span style="font-family: "Courier New", Courier, monospace;"><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">storage-schemas.conf</span></span> file which has entries like:<br />
<span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">[webserver_metrics]</span></span><br />
<span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">pattern = webserver*</span></span><br />
<span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">retentions = 60s:90d 5m:3y</span></span><br />
<br />
which
sets the data intervals and retentions to every 60s for 90d. The
default values are every minute for 24 hours/1 day. Make sure you resize
it or set the defaults before creating it. When Whisper starts to
aggregate the data it requires a certain number of metrics to start the
aggregation. The default is xfactor=.5 which means that at least 50% of
the data points must exist for an aggregated value to be created. If you
have a flaky data injection, you might want to reduce this amount.<br />
<br />
To stop and start carbon:<br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">carbon-cache.py stop</span></span></span></span><br />
<span style="background-color: black;"><span style="color: #eeeeee;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">carbon-cache.py start</span></span></span></span><br />
For more info on this, look at: <a href="https://testing.readthedocs.org/en/latest/monitoring_201.html">https://testing.readthedocs.org/en/latest/monitoring_201.html</a><br />
<br />
Scaling a Graphite system<br />
Graphite
can be clustered to provide data and performance and even up-time at a
scale that isn't possible on a single system. The clustering available
allows spreading data out and/or duplicating data for availability. See
more here: <a href="http://www.aosabook.org/en/graphite.html">http://www.aosabook.org/en/graphite.html </a>jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0tag:blogger.com,1999:blog-4804459376450494896.post-47584365725511747062013-08-12T15:30:00.000-07:002019-03-23T05:11:52.026-07:00elasticsearch BasicsWith any data, there's often a need to search for specific pieces, especially when that data comes grouped like documents. <a href="http://www.elasticsearch.org/">elasticsearch</a> is a great tool for building a search mechanism over your data. This search engine is very easy to start up after downloading: <br />
bin/elasticsearch # add -f to run it in console mode<br />
<b><br /></b>
<b><u>elasticsearch Queries and Testing</u>:</b><br />
<b><span style="font-family: "courier new" , "courier" , monospace;">http://localhost:9200/twitter/_search?pretty</span></b> - simple index level search on the twitter example<br />
<br />
Test the default analyzer: <b><span style="font-family: "courier new" , "courier" , monospace;"> </span></b><br />
<b><span style="font-family: "courier new" , "courier" , monospace;">curl -XGET localhost:9200/_analyze?pretty -d 'Test String and Data'</span></b> # it will drop the "and"<br />
<br />
Test with a specific analyzer: <b><span style="font-family: "courier new" , "courier" , monospace;"> </span></b><br />
<b><span style="font-family: "courier new" , "courier" , monospace;">curl -XGET localhost:9200/index_name/_analyze?analyzer=autocomplete&pretty -d 'New Text'</span></b><br />
<br />
Test with tokenizers/filters: <b><span style="font-family: "courier new" , "courier" , monospace;"> </span></b><br />
<b><span style="font-family: "courier new" , "courier" , monospace;">curl localhost:9200/index_name/_analyze?tokenizer=whitespace&filters=lower-case,engram&pretty -d 'Newer data'</span></b> #two filters, one tokenizer<br />
<br />
Explaining a match: <b><span style="font-size: small;"><span style="font-family: "courier new" , "courier" , monospace;"> </span></span></b><br />
<b><span style="font-size: small;"><span style="font-family: "courier new" , "courier" , monospace;">curl -XGET localhost:9200/index_name/type/document/_explain?pretty&q=quiet</span></span></b><br />
<br />
Validate a search:<br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl localhost:9200/index_name/type/_validate/query?explain&pretty -d '{...}' </b></span>#remember to escape the & on *nix<br />
<br />
<b><u>Setting up and monitoring a cluster:</u></b><br />
cluster.name : nodes with same cluster name will try to form a cluster<br />
node.name : instance name, will be chosen automatically on each start up, but best to set unique one<br />
set num of open files > 32k; set memory to no more than 1/2 of system memory (to allow for disk caching)<br />
<b><span style="font-family: "courier new" , "courier" , monospace;">curl localhost:9200/_cluster/health?pretty</span></b><br />
(to turn this into an alert, grep for status and grep for anything that isn't green - that will give you an indication that elasticsearch is reporting a problem. You might also want to check that 'timed_out' is false.)<br />
To check index or shard level add a parameter to the statement above ?level=indices - see below. <br />
<br />
shutdown whole cluster : <span style="font-family: "courier new" , "courier" , monospace;"><b>curl -XPOST localhost:9200/_cluster/nodes/_shutdown</b></span><br />
<br />
shutdown a node in cluster: <span style="font-family: "courier new" , "courier" , monospace;"><b>curl -XPOST localhost:9200/_cluster/nodes/node_name/_shutdown</b></span><br />
get node names from : <b><span style="font-family: "courier new" , "courier" , monospace;">curl localhost:9200/_cluster/nodes</span></b><br />
<br />
... and cluster/node stats:<br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl localhost:9200/index_name,index_name2/_stats?pretty #in addition, can grep for count to find number of docs </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b><b><span style="font-family: "courier new" , "courier" , monospace;">curl localhost:9200/_cluster/health?pretty&level=indices #add grep for status and grep -v green to turn into alert</span></b></b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b><b><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "courier new" , "courier" , monospace;"><b><b><span style="font-family: "courier new" , "courier" , monospace;">curl localhost:9200/_cluster/health?pretty&level=shards #add grep for status and grep -v green to turn into alert</span></b></b></span> </span></b> </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl localhost:9200/_stats</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl localhost:9200/_nodes # _nodes will also return OS status</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl localhost:9200/_nodes/SpecificNodeName</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl localhost:9200/_nodes/stats ; curl locahost:9200/_nodes/SpecificNodeName/stats</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl localhost:9200/_cluster/state</b></span> #can add ?filter_nodes to remove node info (or filter_blocks,filter_indices, etc)<br />
<br />
<br />
<b><u>Documents</u>:</b><br />
Use: <br />
<ul>
<li>XPUT to put a document with a specific id into the system: localhost:9200/index/type/<b>id</b> -d 'document body'</li>
<li>POST then ES will generate an ID which needs to be read from the respons: localhost:9200/index/type -d 'document body' #id in response body </li>
<li>XGET to retrieve a known document: localhost:9200/index/type/id </li>
</ul>
/_update to update<br />
/_search to query - with a body or like curl localhost:9200/index/_search?q=customerId:20020<br />
<br />
localhost:9200/ -d '{ "query":{"match_all":{}}} #get all data<br />
use PUT is specifying doc id, use POST if want ES to do it; diff types can have diff mappings<br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl -XPUT localhost:9200/entertainment/movies/1 -d '{"movie_title": "The Monsters", "actor":"some nutty guy", "revenue":2000}'</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl -XPUT localhost:9200/entertainment/movies/2 -d '{"movie_title": "Alien Remake", "actor":"some nutty guy", "revenue":150000}'</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl -XPUT localhost:9200/entertainment/movies/flop -d '{"movie_title": "Slugs", "actor":"some nutty guy as bob", "revenue":123}'</b></span> #note the change in document naming style - not always a good idea <br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl -XPOST localhost:9200/entertainment/movies -d '{"movie_title": "Hairslugs", "actor":"bob as guy", "revenue":12300000}'</b></span> # ES will return an id in the response - that's the key to finding this document directly<br />
<br />
<b><span style="font-family: "courier new" , "courier" , monospace;">curl -XGET localhost:9200/entertainment/movies/1</span></b> to retrieve that doc<br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl -XDELETE localhost:9200/entertainment/movies/1</b></span> to remove<br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl -XGET localhost:9200/_search</b></span> - across all indices<br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl -XGET localhost:9200/entertainment/_search</b></span> - across all types in entertainment<br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl -XGET localhost:9200/entertainment/movies/_search</b></span> - doc type movies in entertainment index<br />
<br />
simple query:<br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl -XGET localhost:9200/_search -d '{"query": {"query_string":{"query":"Monsters"}}}'</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl -XGET localhost:9200/_search -d '{"query": {"query_string":{"query":"Monsters", "fields":["movie_title"]}}}'</b></span><br />
<br />
<b><u>Filtered search:</u></b><br />
<b><span style="font-family: "courier new" , "courier" , monospace;">curl -XGET localhost:9200/_search -d '{"query": {"filtered": {"query_string":{"query":"Monsters", "fields":["movie_title"]}}, "filter": { "term": {"revenue":2000}}}}'</span></b><br />
could switch the query to 'match_all' and just filter over everything<br />
or use constant score like this:<br />
'{"query": { "constant_score":{ "filter":{ "term":{"revenue":2000}}}}}'<br />
<br />
<b><u>Mapping example:</u></b><br />
Here is a multi-field (to have normally tokenized analysis and unaltered indexing) mapping (basically a schema) example.<br />
Create the mapping on a new index (you might need to -XDELETE the index if it already exists or try updating an existing index, but you won't be able to update existing documents from what I understand). Below, we're creating a mapping on one type (movies) on an index (entertainment):<br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl -XPUT localhost:9200/entertainment/movies/ -d '{</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b> "movies":{ "properties":{ "actor":{ "type": "multi_field", "fields": {"actor": {"type":"string"}, </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b> "fullname":{"type":"string", "index":"not_analyzed"}}}}} '</b></span><br />
and check your mappings on an index:<br />
<b><span style="font-family: "courier new" , "courier" , monospace;">curl -XGET localhost:9200/entertainment/movies/_mapping</span></b><br />
<br />
sets actor to be analyzed as normal, but also adds actor.fullname as a field that can be search in as a combined bit<br />
<br />
To create a mapping on an entire index (all document types): use a similar query, but include your document types in the mapping:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl -XPUT localhost:9200/entertainment/ -d '{ </b></span><br />
<div class="noescape prettyprint">
<span style="font-family: "courier new" , "courier" , monospace;"><b><span class="str">"mappings" : {</span></b></span></div>
<span style="font-family: "courier new" , "courier" , monospace;"><b> "movies":{ </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b> "properties":{ </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b> "actor":{ </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b> "type": "multi_field", "fields": {</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b> "actor": {"type":"string"}, </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b> "fullname":{"type":"string", "index":"not_analyzed"}</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b> }</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b> }</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b> }</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b> },</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b> "cinemas":</b></span><span style="font-family: "courier new" , "courier" , monospace;"><b><span style="font-family: "courier new" , "courier" , monospace;"><b>{ </b></span></b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b> "properties":{ </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b> "location":{ "type": "string" }</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b> }</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b> }</b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b> </b></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>} '</b></span><br />
Here we're creating two types of documents (entertainment/movies and entertainment/cinemas) using one mapping file. Alternatively, this mapping could be loaded by<br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>curl -XPUT localhost:9200/entertainment -d @mapping.json</b></span><br />
where mapping.json is a file containing the json above.<br />
<br />
<b><u>Warming queries:</u></b><br />
elasticsearch allows you to set warming queries to be run during start up. The queries are defined<br />
via PUTting a query to <span style="font-family: "courier new" , "courier" , monospace;"><b>localhost:9200/index/_warmer/warmer_query_name -d'</b></span>...'<br />
A warming script can be retrieved by GETting it: <span style="font-family: "courier new" , "courier" , monospace;"><b>localhost:9200/index/_warmer/warmer_query_name</b></span><br />
Warming queries can be deleted like any document and can be disabled by putting a setting ( {"index.warmer.enable": false} to localhost:9200/index/_settings<br />
<br />
<u>A number of useful links:</u><br />
<a href="https://github.com/elasticsearch/elasticsearch">https://github.com/elasticsearch/elasticsearch</a> - documentation including the twitter example<br />
<a href="http://elasticsearch-users.115913.n3.nabble.com/help-needed-with-the-query-tt3177477.html#a3178856">http://elasticsearch-users.115913.n3.nabble.com/help-needed-with-the-query-tt3177477.html#a3178856</a><br />
<a href="https://gist.github.com/justinvw/5025854">https://gist.github.com/justinvw/5025854</a> - auto-suggest example using a custom analyzer<br />
<a href="http://www.elasticsearch.org/guide/reference/api/search/term-suggest/">http://www.elasticsearch.org/guide/reference/api/search/term-suggest/</a> - details on the auto-suggest<br />
<a href="http://joelabrahamsson.com/elasticsearch-101/">http://joelabrahamsson.com/elasticsearch-101/</a> - similar to some examples above<br />
<a href="http://exploringelasticsearch.com/">http://exploringelasticsearch.com</a>/ - good, book like resource <br />
<a href="http://www.elasticsearch.org/guide/reference/api/index_/">http://www.elasticsearch.org/guide/reference/api/index_/</a><br />
<a href="http://elasticsearch-users.115913.n3.nabble.com/Performance-on-indices-for-each-language-td4035879.html#a4036048">http://elasticsearch-users.115913.n3.nabble.com/Performance-on-indices-for-each-language-td4035879.html#a4036048</a>jmhttp://www.blogger.com/profile/10864447236058701588noreply@blogger.com0