Pencarian

Rss Posts

 

 

 

Berita pada kategori ‘Aplikasi’

Improving MySQL Insert thoughput

Nov 05, 2010

There are three simple practices that can improve general INSERT throughput. Each requires consideration on how the data is collected and what is acceptable data loss in a disaster.
General inserting of rows can be performed as single INSERT’s for example.

INSERT INTO table (col1, col2, col3) VALUES (?, ?, ?);
INSERT INTO table (col1, col2, col3) VALUES (?, ?, ?);
INSERT INTO table (col1, col2, col3) VALUES (?, ?, ?);

While this works, there are two scalability limitations. First is the network overhead of the back and forth of each SQL statement, the second is the synchronous nature, that is your code can not continue until your INSERT is successfully completed.
The first improvement is to use MySQL’s multi values capability with INSERT. That is you can insert multiple rows with a single INSERT statement. For example:

INSERT INTO table (col1, col2, col3) VALUES (?, ?, ?), VALUES (?, ?, ?), (?, ?, ?);

Depending on how you collect the information to be inserted, you may be able to easily implement this. The benefit, as previously mentioned in The value of multi insert values shows an 88% improvement in performance.
One system variable to be aware of is max_allowed_packet. You may have to adjust this for larger INSERT statements.
Next is the ability to INSERT data based on information already in another table. You can also leverage for example another storage engine like MEMORY to batch up data to be inserted via this approach.

INSERT INTO table (col1, col2, col3) SELECT col1, col2, col3 FROM anothertable

The third option is to batch load your data from a flat file. Depending on how you source the information you are inserting, that may also be a significant improvement in throughput in bulk inserting data.

LOAD DATA [CONCURRENT] INFILE ‘file’
INTO TABLE (col1, col2, col3)

On a closing note, the choice of storage engine can also have a significant effect on INSERT throughput. MySQL also offers other non ANSI options including DELAYED, IGNORE and LOW_PRIORITY. These should definitely be avoided.

Jamis Buck: Design Forces in Ekawada, Part 3

Nov 01, 2010

String figure books are great resources, but they can be incredibly frustrating, too. Even if you?ve never tried to use one, it?s not hard to imagine having a string looped around your fingers, trying to hold the book open with your knee, and then discovering that the next step wraps onto the next page. You somehow need to turn the page without dropping any strings, and without closing or dropping the book.

It is no surprise, then, that this became one of the design forces that drove Ekawada?s construction.

Figure detail shows steps in scrollable table

Now, given the constraints on the size of an iPhone screen, it isn?t feasible (in general) to display all of the steps for a figure in one screen, so there still needs to be some kind of ?next page? operation. However, the touchscreen makes this much easier to do than with a physical book.

This screenshot shows the ?figure detail? view. It gives you a nice big picture of what the final pattern will be, and then a sequential list of the steps to accomplish it. As you can see, the number of steps for ?Osage Diamonds? (a.k.a. ?Jacob?s Ladder?) exceeds the available screen real-estate, and so bleeds off the bottom. But that?s no problem on an iOS device, because you can simply swipe to scroll, and a swipe is much easier to do with strings on your fingers than trying to turn the page of a physical book.

Zoomed detail shows each step in full screen

Further, if you tap any row in the instructions, you?ll be taken to a ?zoomed? view. Here, each instruction is displayed full-screen, including any illustration or clarification for that step. (Not all steps have illustrations or clarifications, but for those that do, you?ll find them in this view.) You?ll also get an indicator of how much further you have to go to the end of the figure, and icons for going to the next (or previous) step.

Why icons? I considered doing a ?swipe? gesture to go to the next page, but really, a tap is easier to do with strings on your hands than a swipe. (The less motion involved, the better.) I tried to strike a balance between size of the icons (a larger target is easier to hit) and maximizing the viewable area for the step (the less often you have to swipe to scroll a step instruction, the better). The size as it is now seemed best, empirically, but later versions may adjust the balance further.

This zoomed view also answers another one of the design forces behind Ekawada: ?I don?t understand what this step is telling me to do.? You don?t want to just add verbosity to the instructions, because more verbosity can actually hurt readabilty. (Just try reading these instructions for the ?Apache Door? figure, if you want a concrete example of what I mean.)

Thus, for steps where the instruction itself may be too terse, or ambiguous, I can use the zoomed view to add an illustration, or extra clarification, without bloating the instruction list. When you need more information, you can get at it easily, but for the common case, you can just scan the instruction list.

Tune in next time, when I?ll address another design force behind Ekawada: ?I remember learning this one figure as a kid, but can?t remember how to do it now.?

Integrating WordPress with SendGrid for better email deliverability

Nov 01, 2010

I?d like to announce a new feature we?re adding to all our hosting plans: integration with SendGrid .

What?
What is Sendgrid? Sendgrid is a service that helps us increase email deliverability, no longer do we, or you the customer need to worry about your outbound email servers being down. On top of that, you also get some really neat statistics, like the ones in the image attached to this post (real stats from our test run). (screenshot)

Why?
Some of our higher traffic websites and their membership systems needed a way to track things like how many of their emails were bouncing, and how often their promotional emails were being opened, this is not a replacement to mailchimp, this is just for inter member communication, and for things like making sure the lost password emails went out and were delivered, opened, clicked etc.

When?
Soon. I?ve started work on a custom plugin that will give you similar stats right within the WordPress admin panel, once it?s out of private beta with the customers who need it the most, it?ll be available to all customers, old and new by default.
It?ll also be available on the WordPress.org plugins directory, and the source code will be on Github as always.

How?
You can find out about the release of this plugin in a few ways.

1. Subscribe to this feed.
2. Subscribe to our newsletter.
3. Become a customer :) .

Adding Google Maps to your Java Application

Oct 17, 2010

Google maps was a useful hit from the moment it went on line. Since then thousands of web pages have added map capability to their sites, courtesy of Google. Let me illustrate how you can add Google maps to your Java application.

Background
Google furnishes its maps via a simple
REST request. Does this mean you need to add some fancy REST framework to your application? Not at all! Java provides all you need right in the standard libraries, and it is very easy to do. (that’s part of the real elegance of REST)

Getting It
To request a map, you start with the following URL:

http://maps.google.com/maps/api/staticmap?

After the question mark, append all of the details you wish to be included in the map, separated by ampersand (&) symbols. For example:

http://maps.google.com/maps/api/staticmap?center=Brooklyn+Bridge,New+York,NY&
zoom=14&size=512x512&maptype=roadmap

This requests a road map centering on the Brooklyn Bridge, in New York City, at zoom level 14, 512×512 pixels in size. There’s a very rich collection of options for specifying and decorating maps, Google has very helpfully made a highly detailed page explaining them all.

As I hope you’ve reasonably guessed; the http request dutifully returns an image of the map requested. That’s all there is to it!

Using It
Happily, Java provides all the resources you need to use this great Google feature right in the Standard Edition. Simply create a java.net.URLConnection, request the content, and generate the map image. If you’ve not done this before, fear not, as Java makes it very easy to do, it looks a bit like this:

URLConnection con = new URL("http://maps...").openConnection();
InputStream is = con.getInputStream();
byte bytes[] = new byte[con.getContentLength()];
is.read(bytes);
is.close();
Toolkit tk = getToolkit();
map = tk.createImage(bytes);
tk.prepareImage(map, -1, -1, null);

The variable map now has the image, ready for presentation! Remarkably easy wasn’t it? That’s all it takes, and you can enjoy the full functionality of Google maps in any Java application.

Want More?
Of course I’m not going to leave you hanging like that! ;-) I’ve written a complete free runnable GoogleMap Java component for you, it’s less than one page of code. Try it out and let me know what you think.

Enjoy,

John

More on Web Sockets and HTML5 in Glassfish

Oct 14, 2010

 In my last blog entry, I described VideoSharing which is an application that uses Web sockets to remote UI events and enable participants to control HTML5 video players remotely. Today, I’d like to share with you a similar type of collaboration application, but this time one that uses other HTML5 features: namely, 2D canvases and client SQL databases. The name of this application is BoardMirror.

 Rather than sharing a video object, like in VideoSharing, BoardMirror shares a canvas object and lets participants draw figures on it. For the sake of simplicity, only two figures can be drawn on a canvas: rectangles and circles. These figures are drawn in a random location inside the canvas and using a random color. Here is a screenshot of the application:

The mirror or collaboration part of the application is the result of copying these figures in all the other canvases that are connected to the server. And, of course, we use Web sockets for this purpose. A Web socket is nothing more than a socket connection that can be established between a (modern) Web browser and a Web container. It provides a low-latency, bi-directional communication parallel to the HTTP channel. It is Ajax/Comet on steroids.

In addition, the BoardMirror application has the ability to store your work of art in a local SQL database in the browser. This is done using one of the new APIs in HTML5. In fact, HTML5 has two options for storing data on the client side: Web Storage and Web SQL Databases. The former is implemented by most browsers and can store name-value pairs; the latter, as suggested by its name, gives you full SQL storage but it’s only implement in some browsers.

Let me start describing the canvas and 2D APIs first. As in the VideoSharing application, we use a JSF facelet to define our main page:

    <html ...>
      ...
      <h:body>
          <h5:canvas width="600" height="400"/>
          <h:outputScript library="js" name="json2.js" target="head"/>
          <h:outputScript library="js" name="app.js" target="head"/>
          <br/>
          <input type="button" value="Rectangle" onclick="APP.drawRectangle()"/>
          <input type="button" value="Circle" onclick="APP.drawCircle()"/>
          <input type="button" value="Clear" onclick="APP.clearCanvas(true)"/>
          <input type="button" value="Load" onclick="APP.loadCanvas()"/>
          <input type="button" value="Save" onclick="APP.saveCanvas()"/>
      </h:body>
   <html>

where the h5:canvas element is defined in the same way as h5:video in the VideoSharing blog. What follows is a JavaScript method that draws a rectangle, sends the rectangle to be drawn remotely and stores the rectangle in a local data structure so that it can saved in a local database, if so requested by the user.

    drawRectangle: function() {
        // Get access to canvas and draw rectangle
        var canvas = this.getCanvas();
        var ctx = canvas.getContext('2d');
        ctx.fillStyle = this.random.color();
        var width = this.random.number(40, 100);
        var height = this.random.number(40, 100);
        var x = this.random.number(0, canvas.width - width);
        var y = this.random.number(0, canvas.height - height);
        ctx.fillRect(x, y, width, height);

        // Mirror rectangle via web sockets and store in array
        var shape = {type: "rectangle", x: x, y: y, width: width,
            height: height, color: ctx.fillStyle};
        this.network.send(JSON.stringify(shape));
        this.shapes[this.shapes.length] = shape;
    },

The code that draws the rectangle should be self explanatory, yet another 2D API! The last three lines of code are there to: create a shape object, send the shape over the network and store the shape in an array, respectively. The network object is initialized in the same way as in the VideoSharing application; only it’s onmessage handler is different:

    websocket.onmessage = function (evt) {
        var shape = JSON.parse(evt.data);
        if (shape.type == "clear") {
            APP.clearCanvas(false);
        } else {
            APP.mirrorShape(shape, false);
        }
    };

Since a canvas can be remotely cleared, there’s a special clear shape type that triggers such a remote event. For all the other shapes (real shapes, that is), the mirrorShape() method is called. This method simply draws the shape in the local canvas (without resending the shape over the network again!).

As stated above, this application also supports storing your canvas in a local database. This is done by storing all the shapes in JSON format. The database logic is implemented in the object returned by the database function shown next.

    var database = function (db) {
      return {
        error: function (err) {
            alert(err.message);
        },
        initialize: function() {
            if (window.openDatabase) {
                db = openDatabase('shapes', '1.0', 'All Shapes', 64 * 1024);
                db.transaction(
                    function (tx) {
                        tx.executeSql('CREATE TABLE shapes (id UNIQUE, shape STRING)')
                    });
                this.load();
            } else {
                alert("Your browser does not support SQL databases");
            }
        },
        load: function() {
            db.transaction(function (tx) {
                tx.executeSql('SELECT * FROM shapes',
                [],
                function (tx, results) {
                    for (var i = 0; i < results.rows.length; i++) {
                        var shape = JSON.parse(results.rows.item(i).shape);
                        APP.shapes[i] = shape;
                    }
                    APP.refreshCanvas();
                },
                this.error)
            } );
        },
        save: function() {
            db.transaction(function (tx) {
                tx.executeSql("DELETE FROM shapes");
                for (var i = 0; i < APP.shapes.length; i++) {
                    tx.executeSql('INSERT INTO shapes (id, shape) VALUES (?, ?)',
                    [i, JSON.stringify(APP.shapes[i])], this.error);
                }
                alert("Canvas has been saved into a local database");
            });
        }
      }
    };

In the initialize() method, the SQL database is opened and a table is created to store the shapes. If the table exists, an error will be ignored since no error handler is provided in the call to tx.executeSql(). The load() method runs a select query without any parameters (notice the "[]") and provides a results handler that parses and stores the shape in the array. The save() method clears the table and then inserts the shape one by one into the database. Note how parameters are passed to fill in the value placeholders denoted by "?" in the SQL string. This should be familiar to anyone that has written JDBC code before.

One interesting aspect of the SQL API is that, following a pattern that is common in Javascript, all operations are asynchronous. This is why functions are passed to methods like db.transaction() and tx.executeSql(). However, the order in which SQL statements are executed follows the execution order of the calls to tx.executeSql(). That is, statements are always queued and run in order —and this is why methods like save() above work.

The Web sockets code in Glassfish that supports this application is identical to that in the VideoSharing application. For more information, the reader is referred to the VideoSharing screencast or to the attached source code bundle. Please note that Web sockets are not enabled by default in Glassfish. To enable them you must execute the following single-line command on your domain:

asadmin set configs.config.server-config.network-config.protocols.protocol.http-listener-1.http.
websockets-support-enabled=true
Attachment Size
boardmirror.jpg 132 KB
BoardMirror.zip 27.12 KB

OSGi-enabled Java EE Applications using NetBeans and GlassFish

Oct 04, 2010

Content available at: http://blogs.sun.com/arungupta/entry/screencast_32_osgi_enabled_java

Eclipse Labs – Most Active Project: Eclipse Labs Projects Update for the week of September 17th 2010

Sep 17, 2010


Most Active Projects

code-recommenders – IDE 2.0: Bringing Collective Intelligence into Software Development
anyedittools – AnyEdit plugin adds several new tools to the context menu of text based Eclipse editors, to Eclipse main menu and editor toolbar
birt-functions-lib – A collection of BIRT Aggregate and Script Function Extensions for use in your project
birt-controls-lib – A collection of BIRT ReportItems created through the ReportItem extension point.
wascana – Wascana Eclipse C/C++ IDE for Windows Developers
ostool – Central metadata repository for unstructured data supports records and knowledge management for small and medium-sized organisations.
onotoa – Onotoa is a visual editor for the Topic Maps Constraint Language.

New Projects

java-toto – sweepstake application
multiproperties – MultiProperties is an Eclipse editor for multiple Java properties files editing simultaneously
splendor023 – splendor023
mc920-reconhecimento-facial – Realce da face
smartedu1 – Education management
smartedu – Education management
hypeerweb – BYU CS 340 Project

Grizzly Deployer (1.9.19) new features

Aug 07, 2010

It has been a while since a blog about Grizzly, I was too busy adding new features. The Grizzly Deployer’s community grows and requested theses new features.

Here a quick list of the changes :

  • Websockets support
  • Watch folder
  • Can starts without applications to deploy
  • Can be embedded and extended easily
  • and few bug fixes (thanks to the community)

A little overview or remainder of what is Grizzly Deployer

Grizzly Deployer is a bundle of Grizzly project that act as a web container. It will allow you to deploy applications (war, servlet).
Works fine for Comet, Atmosphere, JSP applications … For more details, I suggest this link Introduction to the first release of Grizzly Deployer

First, lets take a look at the command line parameters.


Usage: com.sun.grizzly.http.servlet.deployer.GrizzlyWebServerDeployer

  -a, --application=[path]    Application(s) path(s).

                              Application(s) deployed can be :
                              Servlet(s), war(s) and expanded war folder(s).
                              To deploy multiple applications
                              use File.pathSeparator

                              Example : -a /app.war:/servlet/web.xml:/warfolder/

  -p, --port=[port]           Runs Servlet on the specified port.
                              Default: 8080

  -c, --context=[context]     Force the context for a servlet.
                              Only valid for servlet deployed using
                              -a [path]/[filename].xml

  --dontstart=[true/false]    Won't start the server.
                              You will need to call the start method.
                              Useful for Unit testing.
                              Default : false

  --libraryPath=[path]        Add a libraries folder to the classpath.
                              You can append multiple folders using
                              File.pathSeparator

                              Example : --libraryPath=/libs:/common_libs

  --autodeploy=[path]         AutoDeploy to each applications.
                              You could add JSP support.
                              Just add a web.xml that contains Jasper

                              Example : --autodeploy=/autodeploy

  --webdefault=[path]         webdefault to be used by all applications, can be file or dir with multipe web.xmls.
                              If you want to add only one webdefault point it to web.xml file,
                              If you want multiple files to be included put them in one dir and provide this location here.

                              Example : --webdefault=webdefault.xml

  --cometEnabled=[true/false] Starts the AsyncFilter for Comet.
                              You need to active this for comet applications.
                              Default : false

  --websocketsEnabled=[true/false] Starts the AsyncFilter for Websockets.
                              You need to active this for websockets applications.
                              Default : false

  --forceWar=[true/false]     Force war's deployment over a expanded folder.
                              Will deploy the war instead of the folder.
                              Default : false

  --ajpEnabled=[true/false]   Enable mod_jk.
                              Default : false

  --watchInterval=[seconds]   Watch interval to scan for new applications to deploy in work folder.
                              Default : -1 ; disabled

  --watchFolder=[path]        Folder to scan for new applications to deploy in work folder
                              Default : none

Websocket support

The first feature added was Websockets support. It can be enabled at command line by using the parameter –websocketEnabled=true . That will allow you to deploy Websocket applications.

Watch folder

This feature is interesting, because most of the web container have it. Grizzly Deployer will watch a specific folder to applications to deploy. When this feature was added, I remove the need to specify a application to deploy by command line.
To enable this feature you need to use the command line parameter –watchFolder=[path]. It can be used with this other parameter : –watchInterval=[seconds].

The parameter –watchInterval is used to create a watchdog that will monitor the watch folder. If the parameter –watchInterval is not present, when Grizzly Deployer starts, it will check
all applications that are in the watch folder at launch time and will deploy them, but it won’t monitor the folder after that.

When both of theses parameters are used, it will create a watchdog that will deploy and undeploy application put in the watch folder, and remove from that folder. If you deploy an application at command line and put another application with the
same context name in the watch folder, the application in the watch folder won’t be deploy twice.

Embedded Grizzly Deployer

Grizzly Deployer could already be embedded trough his API, but we got few feedback about to make it simplier. The problem wasn’t the API, it just that most people will only use the main part of Grizzly Deployer : deploy a war.

So to make it easier and faster to implement, I did a little refactoring. We now have 2 main config classes we could use to launch an Embedded Grizzly Deployer : DeployerServerConfiguration and DeployableConfiguration.

DeployerServerConfiguration

That file is used to launch Grizzly Deployer. It contains the same command line parameters. In fact, at runtime, the command line parser use that classe.


    GrizzlyWebServerDeployer deployer = new GrizzlyWebServerDeployer();

    DeployerServerConfiguration conf = new DeployerServerConfiguration();
    conf.cometEnabled = true;
    conf.watchFolder="C:/temp/webapps/";
    conf.watchInterval=15;
    deployer.launch(conf);

Once grizzly Deployer is launched in your application, you can deploy a war later. For that, you use this class : DeployableConfiguration.


    DeployableConfiguration warConfig = new DeployableConfiguration();
    warConfig.location = "C:/temp/webapps/grizzly-jmaki.war";

    deployer.deployApplication(warConfig);

You can follow me on Twitter.

Heiko Seeberger: How to setup a Scala project with SBT and IDEA

Aug 07, 2010

Today I helped my mate Bernd Kolb setting up a simple Scala project. He had been struggling far too long with various options like Maven, SBT, Eclipse and Intellij IDEA. If I remember correctly it only took a 30 minutes conf call to setup a simple Scala and Lift project with SBT and IDEA. But there are quite a few things to know in order to be successful which I am going to describe here in a step-by-step tutorial. I hope you find it useful. Please let me know any issues.

Why SBT and IDEA?

Why not Maven and Eclipse? Or Ant and Netbeans? Or …? First, we have to make a choice. Second, SBT and IDEA are currently the best build tool and the best IDE for Scala. SBT is written specifically for Scala and offers very fast compilation, interactive mode for continuous compilation, testing, etc., integration for Scala test tools like specs and ScalaTest, cross compiling against different Scala versions and many more. While the Scala IDE for Eclipse recently has made nice progress and the Netbeans plugin looks good, IDEA still offers the best Scala plugin with many features known from a Java IDE, e.g. refactroings, code completion, code navigation, etc.

Preparations

Java 5 or 6 is assumed. Get the SBT launcher and follow the instructions to set it up. Install IDEA 9.0.3 and use its plugin manager to install the Scala plugin. Here we go!

1. Create a SBT project

Create a folder for your project, cd there and run SBT. You will be asked the following questions. Answer like I did or chose different values (except for the Scala version).

Project does not exist, create new project? (y/N/s) y

Name: simple

Organization: localhost

Version [1.0]:

Scala version [2.7.7]: 2.8.0

sbt version [0.7.4]:

Now SBT will download Scala 2.7.7 as well as some other libraries for internal use. And it will download Scala 2.8.0 which is used to build your project.

As soon as the downloads are finished, you are in SBT’s interactive mode. At the prompt enter compile, then ~test (press enter to leave the triggered mode) and then help to make yourself familiar with the features.

Looking at your project in the file system, you will notice a layout similar to Maven: Scala sources go into src/main/scala, Scala tests go into src/test/scala and artifacts go into target. If your project looks different from the one at the right, you probably did not run compile or test. But as it is important to run your project against the same version as it was compiled against, there is a Scala_2.8.0 folder between the target and the classes, test-classes etc.

Update

Thanks to Comments from Bart and Mikko I looked into the sbt-idea-plugin which makes it possible to create the project files for IDEA from SBT. While it is still under active development and there are some minor issues, the current snapshot release 0.1 already produces usable IDEA project files. So you could just go for that plugin and skip the next two steps.

2. Create an IDEA project

Now you have to create an IDEA project on top of your SBT project.

Choose File > New Project …, select Create project from scratch and continue.

Then pick your project folder for Project files location and continue.

Then enter src/main/scala as path for the source directory and continue.

Now it is getting a little tricky!

Select Scala from the list of technologies.

Ignore the erroneous combo for the Version.

Choose Pick files from disk and select scala-compiler.jar and scala-library.jar form project/boot/scala-2.8.0/lib.

Then change the Name to scala-2.8.0 and click Finish.

3. Adjust IDEA project settings

Now you have make some adjustments to align SBT’s and IDEA’s project layout.

Choose Modules on the left and navigate to the Sources tab. Add src/main/resources to the Sources (blue) and src/test/scala and src/test/resources to the Test Sources (green). Exclude (red) .idea, lib, project/boot and target.

?

Next go to the Paths tab and select target/scala_2.8.0/classes for the Output path and target/scala_2.8.0/test-classes for the Test output path.

?

Then choose the Scala facet from the mid column (below the one and only module simple) and unchek Use Scala compilers ….

That’s it! Now you are ready to go.

4. Give it a try

In IDEA go to the src/main/scala folder and choose New > Scala Class from the context menu. Call it Hello and make it an Object.

In the editor delete the package statement and enter the following code:

object Hello {

??def main(args: Array[String]) {

????println(“Hello!”)

??}

}

From the context menu choose Run Hello.main(). IDEA will build your project and then run this object.

After that go to SBT and enter ~run. Watch the output and look for the Hello! message. Then change the message in the code, e.g. to Hello, world!. As you have told SBT to use triggered run by prefixing whith the tilda, your project will be incrementally built and the Hello object run again: Watch the output.

Have fun with Scala, SBT and IDEA!

Open Source BI Report tools

Aug 06, 2010

I am going to take a mulligan on a previous Blog Post on Open Source BI reporting tools. Part of my job at Calpont is getting familiar with tools folks with use with the InfiniDB storage engine. After all, what is the point of having all those terabytes in a data warehouse if you can not produce some sort of report from them. Three popular open source BI reporting tools are available from BIRT, Pentaho, and Jaspersoft.Proceeding alphabetically, BIRT is part of the Eclipse IDE world. A BI report is a new project or new report under a project. And if you like Eclipse or Java IDEs, then you will probably like BIRT for reports.Pentaho’s Rerport Designer is a stand alone program. As is iReport from Jaspersoft. I had no problems connection to a data mart in a InfiniDB instance with the old JDBC connection with any of the products. And all three produced reports of various levels of complexity from my SQL. Plus you can pretty much format the exams to your heart’s content.So which is better? That would be up to the user having to run the software. For those of you really interested, there will be quick start guides up soon on each of the three BI tools on the Calpont InfiniDB website. If I missed your favorite BI reporting tool, please let me know.Now I am digging through the ETL tools. I was getting a millions and a half rows a second into an InfiniDB table using the cpimport tool. Now to get the tools and cpimport working together!