inicio mail me! sindicaci;ón

Copying Files between S3 buckets

Building on this article here is a simple ruby script, that copies files between two buckets of the same S3 account, omitting files already present (by name).
This variant adds a list of path prefixes, so you can selectively copy only certain directories of your buckets.
Furthermore it copies the original buckets ACLs for each key.

require 'rubygems'
require 'right_aws'

aws_access_key_id     = 'YOUR AMAZON ACCESS KEY'
aws_secret_access_key = 'YOUR AMAZON SECRET ACCESS KEY'
source_bucket         = 'SOURCE BUCKET NAME'
target_bucket         = 'TARGET BUCKET NAME'
prefixes              = [PATH_PREFIX1, PATH_PREFIX2, ...]

s3 = RightAws::S3Interface.new(aws_access_key_id, aws_secret_access_key)

copied_keys = Array.new
(prefixes || ['']).each do |prefix|
  s3.incrementally_list_bucket(target_bucket, {:prefix => prefix}) do |key_set|
    copied_keys << key_set[:contents].map{|k| k[:key]}.flatten
  end
end
copied_keys.flatten!

(prefixes || ['']).each do |prefix|
  s3.incrementally_list_bucket(source_bucket, {:prefix => prefix}) do |key_set|
    key_set[:contents].each do |key|
      key = key[:key]
      if copied_keys.include?(key)
        puts "#{target_bucket} #{key} already exists. Skipping..."
      else

        puts "Copying #{source_bucket} #{key}, setting acl"

        retries=0
        begin
          s3.copy(source_bucket, key, target_bucket)
          acl = s3.get_acl(source_bucket, key)
          s3.put_acl(target_bucket, key, acl[:object])
        rescue Exception => e
          puts "cannot copy key, #{e.inspect}\nretrying #{retries} out of 10 times..."
          retries += 1
          retry if retries <= 10
        end
      end
    end
  end
end

MomoFlow

Coverflow has become a de facto visualization standard for the presentation of collections of images, be it covers or portraits.
There are a number of implementations for usage on web pages (e.g. this one) but the usable ones require Adobes Flash and thus won’t run on the iPhone.

When looking for HTML5 canvas based implementations I found this promising implementation based on the YUI library.
Though workig, it has three major drawbacks: It is rather overengineered and difficult to tweak, it uses YUI (whereas I prefer the more lightweight jQuery) and it performs poorly with image sizes bigger than thumbnails.

After trying to change the code for a while I decided to do a reimplementation in jQuery. The result can be seen on the MomoFlow demo page. Here are two screenshots:

CoverFlow using canvas and jQuery

CoverFlow using canvas and jQuery

Quicklook mode

Quicklook mode

The used 3D transformation is superbly described on the YUI blog .

My implementation caches the rendered canvases per rendering angle. Further speed increments are made possible by adjusting the mesh width used for the slicing transformation depending on the achieved framerate.

The result performs beautifully in recent Safari, Chrome and Opera, decently on Firefox. It also works flawlessly on the iPhone. Keyboard control is coming soon.

I do still need help on IE, maybe the image composition is too demanding for ExplorerCanvas? 
The code is available on github: http://github.com/momolog/momoflow.
Comments and improvements are very much welcome!

MySQL BEFORE INSERT trigger as check constraint

Since MySQL does neither have real check constraints nor a way to raise an exception in a stored procedure, we found it not instantly obvious, how we could *reject* a certain row on insert, based on a certain condition.

A nice way we found was to set the value in question to NULL, based on the condition and let the NOT NULL constraint do its work.

ALTER TABLE sessions MODIFY session_id varchar(255) NOT NULL;
DROP TRIGGER IF EXISTS check_sessionid;
DELIMITER $$
CREATE TRIGGER check_sessionid BEFORE INSERT ON sessions  
FOR EACH ROW BEGIN
  IF NOT NEW.session_id REGEXP '^[[:xdigit:]]{32}$' THEN
    SET NEW.session_id = NULL;
  END IF;
END;
$$
DELIMITER ;

The trigger will let any 32 character string with only HEX characters for the column session_id pass and rejects the rest.

> INSERT INTO sessions (session_id) VALUES ('ffffffffffffffffffffffffffffffff');
Query OK, 1 row affected (0.01 sec)
> INSERT INTO sessions (session_id) VALUES ('fffffffffffffffffffffffffffffffg');
ERROR 1048 (23000): Column 'session_id' cannot be null

Happy triggering.

Vor kurzem dazugelernt:

Suche nach Wort unter dem Cursor in vim: #.

jssh ist eine JavaScript Shell, die den Firefox per Port 9997 fernsteuerbar macht.
Download z.B. hier.

y erzeugt einen YAML dump auf der Rails console, mehr dazu hier.

=3D ist ein escaptes “=” in quoted_printable.

sudo /usr/libexec/locate.updatedb aktualisiert unter MacOSX sofort die locate Datenbank.

rake db:migrate:redo führt unter rails die letzte Migration rückwärts und sofort wieder vorwärts aus, so dass sich die Vorwärts-Action korrigieren läßt

ack -Q bringt ack dazu, literal, also ohne RegExp zu suchen.

MacOSX Boot-Tastenkombinationen: single user, verbose, safe

In den single user mode (root shell) starten, beim Neustart:
[CMS] s

In den verbose mode (boot log) starten:
[CMD] v

In den safe mode (nur core kexts) starten:
[SHIFT]

Javascript function names

Javascript allows naming and assigning functions at the same time like:

var vname = function fname() {}

The function name fname is available only inside the function as a local variable:

var vname = function fname(){
  console.log(typeof vname);  // function
  console.log(typeof fname);  // function
}
console.log(typeof vname);    // function
console.log(typeof fname);    // undefined

If we “redefine” this local variable inside of the function, we get a strange effect:

var vname = function fname(){
  console.log(typeof vname);  // function
  console.log(typeof fname);  // undefined !!!
  var fname = 1;
  console.log(typeof fname);  // number
}
console.log(typeof vname);    // function
console.log(typeof fname);    // undefined

Obviously the interpreter sees the variable declaration var fname on entrance into the function and does not provide the function variable at all.

Accidentally unpacked a jar into home dir?

find . -newerBt '10 minutes ago' -depth 1 -print 2>/dev/null

Atomares Einfügen in PostgreSQL | Teil 2

Die im vorletzten Artikel vorgestellte Lösung hat den Nachteil, den ganzen Table my_table zu locken.
Das ist dann ein Problem, wenn criterion mittels unique constraint eindeutig gemacht wird und nach der Funktion eine zeitaufwendige Funktion (z.B. eine Suche) in derselben Transaktion folgt: Dann warten nämlich alle Prozesse, die ein findOrCreate machen wollen, unabhängig mit welchem Wert für criterion, auf den Abschluss der ersten Transaktion, da erst zu diesem Zeitpunkt sichergestellt werden kann, dass die Spalte wirklich unique ist.

Eigentlich sollte es aber möglich sein, dass parallele Transaktionen mit anderen Werten für criterion unbehelligt ausgeführt werden.
Mit dem folgenden, von Martin Heistermann und Stephan Lüderitz vorgeschlagenen Ansatz, umgeht man dieses Problem elegant:

CREATE OR REPLACE FUNCTION findOrCreate(
  IN criterion_in text,
  OUT record_id integer, OUT is_new bool) AS
$$
DECLARE
BEGIN
    INSERT INTO
      my_table (criterion)
    VALUES
      (criterion_in)
    RETURNING id INTO record_id;
    SELECT true INTO is_new;
EXCEPTION
    WHEN unique_violation THEN
        SELECT false, id into is_new, record_id FROM my_table 
        WHERE criterion=criterion_in;
END
$$ language plpgsql;

Wie in dieser Präsentation (PDF) beschrieben, läßt PostgreSQL das parallele Einfügen verschiedener Werte mittels findOrCreate zu, zwingt aber bei zwei Transaktionen, die denselben Wert einfügen wollen, die zweite, mit dem Insert auf das Ende der ersten zu warten.

Findet die (potentiell teure) Suche in der Transaktion nach findOrCreate statt, wird diese gar nicht mehr ausgeführt, da findOrCreate per Exception die von der ersten Transaktion neu eingefügte Zeile ermittelt und gleich mit deren Ergebnissen weitermachen kann.

Du bist Terrorist.

Kampagne gegen Terroristen in Deutschland. Sehr schön.

Atomares Einfügen in PostgreSQL

Oft steht man vor dem Problem, einen Datensatz anhand eines Kriteriums zu finden bzw. ihn, falls nicht vorhanden, einzufügen. Bei derartigen Operationen ist es wichtig darauf zu achten, dass nicht mehrere Prozesse, die quasi gleichzeitig diesen Datensatz suchen, eine race condition hervorrufen.
Hier eine Lösung mittels stored procedure für postgresql. Dabei wird eine race condition zwischen Suche und Einfügen vermieden und nur minimales Locking gebraucht:

CREATE OR REPLACE FUNCTION findOrCreate(
  IN criterion_in text, 
  OUT id int, OUT is_new bool) AS 
$$
DECLARE 
BEGIN
  LOCK TABLE my_table;
  SELECT false, id FROM my_table WHERE criterion=criterion_in INTO is_new, id;

  IF id IS NULL THEN
    INSERT INTO 
      my_table (criterion) 
    VALUES 
      (criterion_in) 
    RETURNING id INTO id;
    
    SELECT true INTO is_new;
  END IF;

  RETURN;
END
$$ language plpgsql;

Man beachte die praktischen OUT-Parameter, die es ermöglichen, diese Funktion wie folgt aufzurufen:

SELECT id, is_new FROM findOrCreate(criterion);

Rückgabewerte sind id und is_new, die einem die Datenbank-Id angeben, und ob diese neu angelegt wurde.

Der Table-Lock wird beim Beenden der Funktion (die immer implizit eine Transaktion ist) automatisch beendet.

« Previous entries · Next entries »