This won't mean much to anyone but me, but it's the culmination of several weeks of refactoring. This test runs a simple query using both the current storage engine and the new append-only storage engine, by simply choosing which DatabasePackage to use. All the query parsing, optimization, and execution is common, just the low level storage engine is different. While the query is simple, a lot of things have to work at the lower levels to make it function, so it's a good milestone.
This is using jUnit's ability to run tests with multiple sets of parameters.
Saturday, July 30, 2011
Thursday, July 21, 2011
Upgrading to Lion
As much as I know better than to rush to new technology, I couldn't resist upgrading to Lion right away.
I wasn't really paying attention, but it took about an hour to download the 4gb update from the App Store.
I knew I had four machines to update (my iMac and MacBook, and Shelley's iMac and MacBook) so I looked up online how to update multiple machines from a single download.
The trick is that there is a disk image inside the download that you can use to make a bootable DVD or USB drive. But you need to do that after you download, but before you install. One of the better explanations was HOW TO: Do a Clean Install of OS X Lion
Without thinking, I started burning a DVD, only to realize that the MacBook Air's don't have DVD drives. So I grabbed the thumb drive I had handy and tried to use it. But it was only 4gb and the image didn't fit. I thought I'd have to get a bigger thumb drive but then I remembered I had a small USB hard disk that I use for backing up photos when travelling. It was more than big enough.
The install went smoothly. Again, I didn't time it, but it was something like half an hour.
The next morning, when I tried to start Eclipse to do some programming, I got "no JRE installed" and it asked if I wanted to install one. I said yes and after quite a long pause (30 seconds?) it installed one and Eclipse started. Not bad!
I needed to reselect the JRE in Eclipse. Then a test failed because I'd forgotten to enable asserts on that JRE. But after that, all the tests passed. So far so good.
Then I found Mercurial was broken. A quick internet search found Mercurial SCM (hg) fix for OS X 10.7 Lion. Even better, I read to the bottom before I tried the fix and found there was an updated installer for Mercurial on Lion. I downloaded and installed it and Mercurial was back in business.
It did take quite a while to re-index for Spotlight searching but that happened in the background.
iTunes gave me a rather frightening message about not being able to save my music library, but it seems to be working fine.
Those are really the only problems I've had so far. Not too bad, considering.
I updated my MacBook using the USB hard disk. It went smoothly also.
One change that is going to take some getting used to is the reversing of the scroll direction. Admittedly, the old way does seem backwards - pulling your finger down to scroll up. Now it's the same as the iPhone or iPad, with a metaphor of dragging the page around. But old habits are hard to break and I keep trying to scroll the wrong direction. And I still use Windows at work which will be the opposite way, although with a mouse wheel rather than swiping on a Magic Mouse.
I can't imagine Microsoft having the guts to make this kind of change. Nor can I imagine Microsoft users standing for it - they would just flip the setting to "Classic" mode. (The same way most of my staff immediately switched new versions of Windows to the old Classic appearance, despite my protests.) It would be interesting to know how many Mac users switch it back to the old way.
There will probably be other issues I haven't run into yet, but so far it's been a pretty smooth update.
I wasn't really paying attention, but it took about an hour to download the 4gb update from the App Store.
I knew I had four machines to update (my iMac and MacBook, and Shelley's iMac and MacBook) so I looked up online how to update multiple machines from a single download.
The trick is that there is a disk image inside the download that you can use to make a bootable DVD or USB drive. But you need to do that after you download, but before you install. One of the better explanations was HOW TO: Do a Clean Install of OS X Lion
Without thinking, I started burning a DVD, only to realize that the MacBook Air's don't have DVD drives. So I grabbed the thumb drive I had handy and tried to use it. But it was only 4gb and the image didn't fit. I thought I'd have to get a bigger thumb drive but then I remembered I had a small USB hard disk that I use for backing up photos when travelling. It was more than big enough.
The install went smoothly. Again, I didn't time it, but it was something like half an hour.
The next morning, when I tried to start Eclipse to do some programming, I got "no JRE installed" and it asked if I wanted to install one. I said yes and after quite a long pause (30 seconds?) it installed one and Eclipse started. Not bad!
I needed to reselect the JRE in Eclipse. Then a test failed because I'd forgotten to enable asserts on that JRE. But after that, all the tests passed. So far so good.
Then I found Mercurial was broken. A quick internet search found Mercurial SCM (hg) fix for OS X 10.7 Lion. Even better, I read to the bottom before I tried the fix and found there was an updated installer for Mercurial on Lion. I downloaded and installed it and Mercurial was back in business.
It did take quite a while to re-index for Spotlight searching but that happened in the background.
iTunes gave me a rather frightening message about not being able to save my music library, but it seems to be working fine.
Those are really the only problems I've had so far. Not too bad, considering.
I updated my MacBook using the USB hard disk. It went smoothly also.
One change that is going to take some getting used to is the reversing of the scroll direction. Admittedly, the old way does seem backwards - pulling your finger down to scroll up. Now it's the same as the iPhone or iPad, with a metaphor of dragging the page around. But old habits are hard to break and I keep trying to scroll the wrong direction. And I still use Windows at work which will be the opposite way, although with a mouse wheel rather than swiping on a Magic Mouse.
I can't imagine Microsoft having the guts to make this kind of change. Nor can I imagine Microsoft users standing for it - they would just flip the setting to "Classic" mode. (The same way most of my staff immediately switched new versions of Windows to the old Classic appearance, despite my protests.) It would be interesting to know how many Mac users switch it back to the old way.
There will probably be other issues I haven't run into yet, but so far it's been a pretty smooth update.
Wednesday, July 20, 2011
Java BufferedInputStream Problem
I ran into a problem loading a large database dump from cSuneido into jSuneido.
After several hours of debugging, I found if I changed this:
InputStream fin = new BufferedInputStream(
new FileInputStream(filename));
to:
InputStream fin = new FileInputStream(filename);
(i.e. removed the BufferedInputStream)
Then it worked ?!
I searched on the web for BufferedInputStream problems but didn't find anything relevant.
One thing that is suspicious is that this is probably the first file I've tried to load that is larger than 4 gb (i.e. the limit for a 32 bit integer), although I'm not sure why that would be a problem for BufferedInputStream. You'd think it would just be reading sequentially. Unless it's trying to do something clever like memory mapping the whole file.
Note: This is on a 64 bit JVM.
I'm just going to omit the BufferedInputStream for now, but it would be nice to know why there was a problem.
I guess I could go dig up the OpenJDK code for BufferedInputStream but I'm not that motivated.
Anybody have any information or thoughts on this?
After several hours of debugging, I found if I changed this:
InputStream fin = new BufferedInputStream(
new FileInputStream(filename));
to:
InputStream fin = new FileInputStream(filename);
(i.e. removed the BufferedInputStream)
Then it worked ?!
I searched on the web for BufferedInputStream problems but didn't find anything relevant.
One thing that is suspicious is that this is probably the first file I've tried to load that is larger than 4 gb (i.e. the limit for a 32 bit integer), although I'm not sure why that would be a problem for BufferedInputStream. You'd think it would just be reading sequentially. Unless it's trying to do something clever like memory mapping the whole file.
Note: This is on a 64 bit JVM.
I'm just going to omit the BufferedInputStream for now, but it would be nice to know why there was a problem.
I guess I could go dig up the OpenJDK code for BufferedInputStream but I'm not that motivated.
Anybody have any information or thoughts on this?
Tuesday, July 19, 2011
Large Scale Java Design
I ran into a problem when I reached the point where I wanted to start integrating my append-only database storage engine into the rest of Suneido.
The problem was that the old storage engine was too tightly coupled with the rest of Suneido. Not very good design on my part, but in my defense, the design came from the old C++ code, much of which is 10 or 15 years old.
So how should the code be structured? What's the best way to use Java packages and interfaces to reduce coupling? I looked for the Java equivalent of Large Scale C++ Software Design by John Lakos. (Seeing that it was published in 1996, I guess I don't really have an excuse for the poor structure of my old code!) But there doesn't seemed to be an equivalent for Java. (Let me know if you have suggestions.)
There are books like Effective Java but they're mostly about small scale. And there are books about J2EE architecture, but I'm not using J2EE.
Books like Growing Object-Oriented Software (recommended) talk about coupling from the view point of testability, but don't explicitly give guidelines for high level architecture.
In addition, what I needed wasn't just less coupling. I needed to be able to configure the code to run with either storage engine. For that, there couldn't be any static coupling between the storage engine and the rest of the code.
One really crude way to do this would be to simply rename one of the two storage engine packages to be the name referenced by the rest of the code. That would require changing every source file. Eclipse would do that, but it wouldn't play well with version control. And I would have to manually ensure that the two packages were defined the same public methods.
Instead I decided to clean up my architecture. In case it's helpful to anyone else, I'll sketch out what I came up with. Some of it is standard advice, some is a little more specific to what I needed. None of it is particularly original or unique.
First, other packages (i.e. the rest of the system) should only reference interfaces that are defined in a separate interface package.
That means no direct field access, you have to use setters and getters for public access.
It also means no public static methods or constructors. Instead there is an interface for a "package" object. A singleton instance of the package object is created at startup. All access to the package starts here. So to use the new storage engine you just have to use its package object. This class contains any public constructors and static methods and is the only public class in the package.
One mistake I made, coming new to Java from C++, was to make methods either private or public. I should have been using package scope a lot more than public. (Which might explain why it's the default in Java!) Now, the only methods that are public are the ones implementing public interfaces.
It's nice that Java (since version 5 / 1.5) allows covariant return types. i.e. the public interface can say that a method returns another public interface, but the actual method implementation can return the concrete type. This reduces the need for casting within the package.
I've finished refactoring so the old storage engine code follows these guidelines. I thought I might run into areas that would take a lot of redesign, but it's gone surprisingly smoothly.
Next I have to refactor the new append-only storage engine to implement the same interfaces. Then I should be able to easily switch between the two.
Of course, the interface I've extracted from the old storage engine doesn't match the new storage engine so I'll have to do some refactoring to end up with a common interface.
As usual, the code is public in Suneido's Mercurial repository on SourceForge.
The problem was that the old storage engine was too tightly coupled with the rest of Suneido. Not very good design on my part, but in my defense, the design came from the old C++ code, much of which is 10 or 15 years old.
So how should the code be structured? What's the best way to use Java packages and interfaces to reduce coupling? I looked for the Java equivalent of Large Scale C++ Software Design by John Lakos. (Seeing that it was published in 1996, I guess I don't really have an excuse for the poor structure of my old code!) But there doesn't seemed to be an equivalent for Java. (Let me know if you have suggestions.)
There are books like Effective Java but they're mostly about small scale. And there are books about J2EE architecture, but I'm not using J2EE.
Books like Growing Object-Oriented Software (recommended) talk about coupling from the view point of testability, but don't explicitly give guidelines for high level architecture.
In addition, what I needed wasn't just less coupling. I needed to be able to configure the code to run with either storage engine. For that, there couldn't be any static coupling between the storage engine and the rest of the code.
One really crude way to do this would be to simply rename one of the two storage engine packages to be the name referenced by the rest of the code. That would require changing every source file. Eclipse would do that, but it wouldn't play well with version control. And I would have to manually ensure that the two packages were defined the same public methods.
Instead I decided to clean up my architecture. In case it's helpful to anyone else, I'll sketch out what I came up with. Some of it is standard advice, some is a little more specific to what I needed. None of it is particularly original or unique.
First, other packages (i.e. the rest of the system) should only reference interfaces that are defined in a separate interface package.
That means no direct field access, you have to use setters and getters for public access.
It also means no public static methods or constructors. Instead there is an interface for a "package" object. A singleton instance of the package object is created at startup. All access to the package starts here. So to use the new storage engine you just have to use its package object. This class contains any public constructors and static methods and is the only public class in the package.
One mistake I made, coming new to Java from C++, was to make methods either private or public. I should have been using package scope a lot more than public. (Which might explain why it's the default in Java!) Now, the only methods that are public are the ones implementing public interfaces.
It's nice that Java (since version 5 / 1.5) allows covariant return types. i.e. the public interface can say that a method returns another public interface, but the actual method implementation can return the concrete type. This reduces the need for casting within the package.
I've finished refactoring so the old storage engine code follows these guidelines. I thought I might run into areas that would take a lot of redesign, but it's gone surprisingly smoothly.
Next I have to refactor the new append-only storage engine to implement the same interfaces. Then I should be able to easily switch between the two.
Of course, the interface I've extracted from the old storage engine doesn't match the new storage engine so I'll have to do some refactoring to end up with a common interface.
As usual, the code is public in Suneido's Mercurial repository on SourceForge.
Tuesday, July 12, 2011
The LMAX Architecture
A good article by Martin Fowler on a very interesting architecture.
Thursday, July 07, 2011
Dreaded Deadlock
I was doing some refactoring and came across a stress test that I hadn't run for a while. (it's too slow to include in my unit test suite)
I ran it and it hung up. What the ...?
I thought maybe it was my refactoring but I reverted my changes and it still hung.
The test is multithreaded and using the Eclipse debugger I could see the threads were all hung at the some spot. It wasn't an infinite loop, but it was a synchronized method.
I used jConsole's deadlock detector to confirm what was going on.
Suspiciously, I had recently made a "minor" change to this exact part of the code.
The funny part was that I made the change because FindBugs reported a potential concurrency problem.
I removed the synchronized and used volatile instead and that fixed the problem. I think volatile is ok in this instance, but it's always hard to know for sure.
I think FindBugs was correct that there was a problem. My mistake was slapping in a fix without really thinking it through. That's definitely a no-no when it comes to concurrency. It's a good example of how the "intuition" that adding more locks can't hurt is very wrong.
My second mistake was not running the related stress tests when I made the change. I need to add the stress tests to our continuous build system at work. And maybe set up something at home as well, if I can make it lightweight enough.
Yet another reminder that concurrency is hard! (not that I should have needed the reminder)
I ran it and it hung up. What the ...?
I thought maybe it was my refactoring but I reverted my changes and it still hung.
The test is multithreaded and using the Eclipse debugger I could see the threads were all hung at the some spot. It wasn't an infinite loop, but it was a synchronized method.
I used jConsole's deadlock detector to confirm what was going on.
Suspiciously, I had recently made a "minor" change to this exact part of the code.
The funny part was that I made the change because FindBugs reported a potential concurrency problem.
I removed the synchronized and used volatile instead and that fixed the problem. I think volatile is ok in this instance, but it's always hard to know for sure.
I think FindBugs was correct that there was a problem. My mistake was slapping in a fix without really thinking it through. That's definitely a no-no when it comes to concurrency. It's a good example of how the "intuition" that adding more locks can't hurt is very wrong.
My second mistake was not running the related stress tests when I made the change. I need to add the stress tests to our continuous build system at work. And maybe set up something at home as well, if I can make it lightweight enough.
Yet another reminder that concurrency is hard! (not that I should have needed the reminder)
Subscribe to:
Posts (Atom)