Wednesday, October 31, 2007

An Inconvenient Truth

Lot friends of mines were telling me that I should watch that movie (also in wikipedia). I recently had the chance! And it really worth!

It's a documentary film about climate change, specifically global warming, presented by former United States Vice President Al Gore. There are lots of numbers and facts that can make you reconsider some aspects of your living.

So I urge you to watch this movie!

MySQL Tips & Tricks /MAX_ROWS & AVG_ROW_LENGTH/

Hello again ;)
I ran into a tricky situation last couple of days. I have some big tables with varchar fields where I have to JOIN ON varchar fields. The problem does not concern only the JOIN - it is a general problem using varchar / text fields used to be searched one way or another.

So - the situation: We have one table with about 1M rows (relatively small amount data for each row ~90 bytes) on one hand. And another table with records to be matched - about 100k rows (relatively medium amount of data for each row ~ 500 bytes). We do a SELECT which JOINs the two tables based on VARCHAR columns. Do not ask "why?" - This is the business logic; it cannot be otherwise (at least in the beginning). If we index the columns we will got the results fast! What is fast? Less than 50 ms to get around 200 joined rows. Well, OK, you got me - it depends strongly on the hardware configuration and MySQL settings. Let's assume this is our limit. I was very happy to discover that it works that fast. The speed is critical, because we will need to join around 100k rows.

Where is the problem then? Well, it happened that the first table is populated with another several hundred thousand records and it became with about 1.9M rows. And? And, it appeared that the MySQL should copy the temp table on the hard drive. As you can guess - the result is: 204 joined rows (not 204k, but only two hundred and four) for ..... 417 seconds. What about 100k rows? Killing me.

What can we do about that? Well, I know (the business logic predicts) that the first table - that with about 2M rows would never have more than 4 million rows. I also noticed that partitioning is only partially supported in the beta (RC yet) of MySQL 5.1.x, but the recommended for production environment is 5.0.x which does not support partitioning. So, I discovered the great options MAX_ROWS and AVG_ROW_LENGTH. Which does what - it says to the MySQL engine, that this table would never have more than MAX_ROWS tows, and the average row length would be AVG_ROW_LENGTH. You can find the average row length by executing the command SHOW TABLE STATUS FROM [DB_NAME] LIKE 'TBL_NAME'. So, if you know, if your business logic predicts that you will have a large table, BUT this table will have relatively known content (like it will have not more than 4M rows), and you know the average row length, you can set the MAX_ROWS and AVG_ROW_LENGTH for that table. The AVG_ROW_LENGTH is required if you have blob/text/varchar fields.

That will cause the MySQL engine to optimize the space required for that table, also optimizing the myisam_data_pointer_size, which sets the number of bytes used for internal row pointers. Yes - it works only with MyISAM tables. But you will be surprised. After I set the MAX_ROWS and AVG_ROW_LENGTH on that table with the 2M records - I got a surprising results - 90k joined rows for ..... 47 seconds !!

I think I will use these options often in the future, what about you?

Friday, September 14, 2007

Skype is sniffing ? You must be kidding me!

Guess what! The little tricks of skype does not have an end ... The Linux users of Skype recently discovered that it is reading the /etc/passwd and firefox profile files. COME ON? Do you continue using Skype?

The original post is here: http://forum.skype.com/index.php?showtopic=95261
And some explanations can be found here: http://www.oreillynet.com/sysadmin/blog/2007/08/a_close_look_at_questionable_a.html

But I seriously consider using Skype ...

Do you?

Tuesday, September 11, 2007

FileSystemWatcher problem

Did you have a task for monitoring files?
The FileSystemWatcher class has a great features, but also some outages. The problem is that creating/copying/moving files arround is hard taks and the watcher raises couple of events.
The most important events in my case were "created" and "changed". The problem is that the "Created" event is fired when the file is started being created, not when finished being created. After the created, there would several "changed" events depending on the file size. So how could we possibly know when the file is realy created, full and ready to work with it?

There are several posts and examples arround the net like:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2123580&SiteID=1&mode=1
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=659823&SiteID=1

What is my approach?
After catching any event - you just stop monitoring for FSWEvents and start a timer. On every tick you try to open the file for reading. If you cannot - increase the timer interval. You can also add a code to monitor the total time elapsed since the first try to open the file, but the provided code does not do that.

So here it is - one of the approached to more correctly monitor for files:

 

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security;
using System.Timers;

namespace MonitorTest
{
    public class Program
    {
        private FileSystemWatcher _watch;
        private string _watchPath = @"d:\temp\";
        private string _watchFile = "SomeFile.txt"
        private Timer _timer;
        private DateTime _started;
        private TimeSpan _totalTime;
        private void StartTrying()
        {
            _started = DateTime.Now;
            _totalTime = TimeSpan.Zero; 
            _timer = new Timer();
            _timer.Interval = 1000;
            _timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
            _timer.Enabled = true;
        } 
        void _timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            string FullPath = string.Format("{0}{1}", _watchPath, _watchFile);
            FileInfo fi = new FileInfo(FullPath);
            StreamReader r = null;
            _totalTime = DateTime.Now - _started; 
            Console.WriteLine("Trying? {0}", _totalTime);
            try
            {
                r = fi.OpenText();
                r.Close();
                r.Dispose();
                _timer.Enabled = false;
                Console.WriteLine("File is free for reading after: {0} timespan?",_totalTime);
            }
            catch (IOException ioex)
            {
                Console.WriteLine("Cannot open ? {0}", ioex.Message);
                _timer.Enabled = true;
                _timer.Interval += 1000;
            }
            finally
            {
                if (r != null)
                {
                    r.Close();
                    r.Dispose();
                }
            }
        } 
        private void OnChanged(object source, FileSystemEventArgs e)
        {
            // Specify what is done when a file is changed, created, or eleted.
            FileSystemWatcher wo = source as FileSystemWatcher;
            FileInfo fi = new FileInfo(e.FullPath); 
            if (wo != null)
            {
                switch (e.ChangeType)
                {
                    case WatcherChangeTypes.Changed:
                        Console.WriteLine("File {0}{1} was changed? {2} 3}", _watchPath, _watchFile, fi.LastAccessTime ,wo.NotifyFilter);
                        StartTrying();
                        _watch.EnableRaisingEvents = false;
                        break;
                    case WatcherChangeTypes.Renamed:
                        Console.WriteLine("File {0}{1} was renamed? {2}",
_watchPath, _watchFile, source);
                        StartTrying();
                        _watch.EnableRaisingEvents = false;
                        break;
                    case WatcherChangeTypes.Deleted:
                        Console.WriteLine("File {0}{1} was deleted? {2}",
_watchPath, _watchFile, source);
                        StartTrying();
                        _watch.EnableRaisingEvents = false;
                        break;
                    case WatcherChangeTypes.Created:
                        Console.WriteLine("File {0}{1} was created? {2}", _watchPath, _watchFile, source);
                        StartTrying();
                        _watch.EnableRaisingEvents = false;
                        break;
                }
            }
        } 
        public Program()
        {
            _watch = new FileSystemWatcher(_watchPath);
            _watch.Filter = _watchFile;
            _watch.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName;
            _watch.Changed += new FileSystemEventHandler(OnChanged);
            _watch.Created += new FileSystemEventHandler(OnChanged);
            _watch.Deleted += new FileSystemEventHandler(OnChanged);
            _watch.EnableRaisingEvents = true; 
        } 
        static void Main(string[] args)
        {
            Program p = new Program();
            Console.ReadLine();
        }
    }
}

Wednesday, September 5, 2007

Another SKYPE issue ...

Do you know the Debug View ?
It is a very usefull application by Microsoft. You can watch for all debug info sent to the "Debug output".

So, I was looking for debug output from some of my projects and I noticed some strange "PING/PONG" messages. The next thing I saw was some names from my Skype's contact list. Amazing ...

Shouldn't they build Skype end product in "Release" configuration and suppress all these debug output messages ?

skype