<![CDATA[Developers Diary]]> https://mspi.es https://mspi.es/favicon.ico Developers Diary https://mspi.es RSS for Node Sat, 27 Apr 2024 06:13:01 GMT Wed, 27 Apr 2016 00:15:00 GMT 30 <![CDATA[5 Performance Tweaks for Visual Studio 2015 and large solutions]]> In a current project I'm working in a large Visual Studio 2013 solution with around 160 C# projects. The latest version of ReSharper is installed and its Solution-Wide Analysis is activated and operates fluent at most of the time. Everything is working fine and coding is fun for everyone in the team.

Then I tried to switch over to Visual Studio 2015 to be able to benefit of the new C# 6 Language Features and other improvements of the IDE itself. Sadly, the experience was extremely horrible. Visual Studio had a CPU load of 15-20% when it should be idle, crashed uncountable times and constantly hung and froze. Fucking hell, did Microsoft mess up the IDE?!

After some research, I found out, that the crashes occurred due to low memory. I had a feeling that this has either to do something with the new Roslyn-based DotNetAnalyzers (successor of the Code Style Analysis Tool StyleCop) or the new Roslyn-based solution analysis or both.

And it looks like my feeling was right. The first optimization below had the biggest impact. Everyone in the team is now able to code in Visual Studio 2015 at the same speed as in Visual Studio 2013. But take a look at the further improvements, too. They'll help you in special scenarios, like editing XAML files, debugging and so on.

1. Disable full solution analysis

This one disables the new Roslyn-based solution analysis. No more CPU-load in the background, no more crashes, due to low memory. With this feature Visual Studio 2015 was usable again. If you have ReSharper installed and Solution-Wide Analysis activated, you won't miss this feature at all. I did need a restart of Visual Studio to see the improvement.

This option can be found under:

Options -> Text Editor -> C# -> Advanced -> Enable full solution analysis

Visual Studio 2015 Performance - Disable Full Solution Analysis

2. Disable XAML Designer

For bigger and more complex solutions than "Hello World" scenarios the XAML-Designer doesn't work correctly. Even worse, it hangs from time to time while editing XAML code and will slow you down. Another pain is, that it could mess up your XAML file when you accidentally move elements in it. If you can live without it, then do yourself a favor and disable it.

This option can be found under:

Options -> XAML Designer -> General -> Enable XAML Designer (requires restart of Visual Studio)

Visual Studio 2015 Performance - Disable XAML Designer

3. Disable CodeLens

To me, CodeLens was always too distracting. While coding I want to focus on code, not more or less. So it is always turned off on my system. Other team members observed a performance gain when they turned the option off.

This option can be found under:

Options -> Text Editor -> All Languages -> Enable CodeLens

Visual Studio 2015 Performance - Disable CodeLens

4. Disable Diagnostic Tools while debugging

The new Diagnostic Tools for Visual Studio look quite nice and I think, that they might be helpful when I'm focused on optimizing performance. But I don't want to run them always when I start a debug session.

This option can be found under:

Options -> Debugging -> General -> Enable Diagnostic Tools while debugging

Visual Studio 2015 Performance - Disable Diagnostic Tools while debugging

5. Enable ReSharper Build

If you've installed ReSharper on your system, you could also give ReSharper Build a try. It uses some heuristics to determine if a project should be built or not. I've enabled it, but I don't see a performance gain or loss. Find out by your own, if it helps you.

This option can be found under:

ReSharper Options -> Tools -> Build -> ReSharper Build

Visual Studio 2015 Performance - Enable ReSharper Build


I hope that you could benefit from the optimizations. Try to enable them one by one to recognize the setting with the most impact.

What do you think? Did they help you, too? Or do you have more tips or tweaks you could share? Leave a post below.


]]>
https://mspi.es/blog/5-Performance-Tweaks-for-Visual-Studio-2015-and-large-solutions 2016-04-27-00-15 Wed, 27 Apr 2016 00:15:00 GMT
<![CDATA[Mein Fachartikel in der dotnetpro: Unter die Haube geschaut - Teil 3]]> Der dritte und letzte Teil meiner Fachartikel-Serie "Unter die Haube geschaut" ist ab jetzt in der Zeitschrift dotnetpro (Ausgabe 3/2016) am Kiosk erhältlich.


dotnetpro-Artikel - Unter die Haube geschaut - Teil 3


Im Artikel wird gezeigt, wie der C# 6-Compiler die neuen Sprachfeatures, die dich bei deiner alltäglichen Arbeit unterstützen, umsetzt. Es werden using static, nameof, der Null-Conditional Operator, Interpolated Strings, Method Expression Body Definitions, Property Expression Body Definitions und Auto-Property Initializers behandelt.

Vermutlich kennst du bereits alle genannten Features, da du sie ständig verwendest. Aber was genau im Hintergrund beim Kompilieren erstellt wird, darüber denkt man eher selten nach, oder? Im Artikel wird der Compiler ein bisschen entzaubert und nebenbei aufgedeckt, wie mächtig oder unmächtig die Common Intermediate Language in Wirklichkeit ist.

]]>
https://mspi.es/blog/Mein-Fachartikel-in-der-dotnetpro-Unter-die-Haube-geschaut-Teil-3 2016-02-24-18-00 Wed, 24 Feb 2016 18:00:00 GMT
<![CDATA[Mein Fachartikel in der dotnetpro: Unter die Haube geschaut - Teil 2]]> Der zweite Teil meiner Fachartikel-Serie "Unter die Haube geschaut" ist ab jetzt in der Zeitschrift dotnetpro (Ausgabe 2/2016) am Kiosk erhältlich.


dotnetpro-Artikel - Unter die Haube geschaut - Teil 2


Der Artikel deckt auf, an welchen Stellen der C#-Compiler dich bei deiner alltäglichen Arbeit unterstützt und was geschieht, wenn du Sprachfeatures wie Lambda Methods, Lambda Expressions, dynamic, async/await oder Caller Information Attributes verwendest.

Vermutlich kennst du bereits alle genannten Features, da du sie ständig verwendest. Aber was genau im Hintergrund beim Kompilieren erstellt wird, darüber denkt man eher selten nach, oder? Im Artikel wird der Compiler ein bisschen entzaubert und nebenbei aufgedeckt, wie mächtig oder unmächtig die Common Intermediate Language in Wirklichkeit ist.

Der abschließende dritte Teil wird in Ausgabe 3/2016 veröffentlicht und beschäftigt sich mit den neuen Sprachfeatures von C# 6. Mehr dazu erfährst du im nächsten Monat.

]]>
https://mspi.es/blog/Mein-Fachartikel-in-der-dotnetpro-Unter-die-Haube-geschaut-Teil-2 2016-01-22-18-00 Fri, 22 Jan 2016 18:00:00 GMT
<![CDATA[Mein Fachartikel in der dotnetpro: Unter die Haube geschaut - Teil 1]]> Mein dritter Fachartikel, diesmal der erste Teil einer dreiteiligen Serie, wird am 17.12. in der Zeitschrift dotnetpro (Ausgabe 1/2016) am Kiosk erscheinen.


dotnetpro-Artikel - Unter die Haube geschaut - Teil 1


Im Artikel zeige ich, was der C#-Compiler dir an Arbeit abnimmt und welcher Code generiert wird, wenn du Sprachfeatures wie Iterators, Generators/yield, Anonymous Types, Partial Classes, Nullables oder Collection Initializer verwendest.

Vermutlich kennst du bereits alle genannten Features, da du sie ständig verwendest. Aber was genau im Hintergrund beim Kompilieren erstellt wird, darüber denkt man eher selten nach, oder? Im Artikel wird der Compiler ein bisschen entzaubert und nebenbei aufgedeckt, wie mächtig oder unmächtig die Common Intermediate Language in Wirklichkeit ist.

Der zweite Teil wird in Ausgabe 2/2016 veröffentlicht und beschäftigt sich mit einigen Sprachfeatures von C# 3-C# 5. Mehr dazu gibt's aber in einem weiteren Blogbeitrag.

]]>
https://mspi.es/blog/Mein-Fachartikel-in-der-dotnetpro-Unter-die-Haube-geschaut-Teil-1 2015-12-15-18-00 Tue, 15 Dec 2015 18:00:00 GMT
<![CDATA[Developer OpenSpace in Leipzig]]> Bald ist es endlich wieder soweit mit meinem persönlichen Highlight gegen Ende des Jahres. Die Unkonferenz Developer OpenSpace in Leipzig beginnt nächste Woche (vom 16.10.-18.10.). Den Auftakt bildet der Workshop-Freitag mit über 20 verschiedenen interessanten Themen, wie zum Beispiel Graphdatenbanken, Docker, Micro Services, Roslyn und so weiter. Ich habe mich dieses Jahr für Micro Services in der Praxis von Mike entschieden. Viele Workshops waren bereits am Tag, an dem die Anmeldung freigeschalten wurde, ausgebucht. Die eigentliche OpenSpace findet am Samtag und Sonntag statt. Es werden über 200 Entwickler/Designer/Leiter aus allen Teilen Deutschlands anreisen.

Ich habe oben "Unkonferenz" geschrieben, weil es sich hier nicht um ein gewöhnliches Konferenz-Konzept handelt, sondern hier die Teilnehmer selbst die Themen vorschlagen um letztendlich gemeinsam darüber zu diskutieren. Falls man möchte, dann kann man auch einfach eine Präsentation halten. Die Gestaltung einer Session ist komplett frei und wird entscheident durch die Stimmung der Gruppe getrieben. Es gilt auch immer das Gesetz der zwei Füße, welches besagt, dass man einfach den Raum wechseln kann, wenn man nichts mehr beitragen oder lernen kann.

Wenn du interessiert bist, dann schau doch einfach mal vorbei. Anmelden kannst du dich hier.

Developer OpenSpace in Leipzig: https://devopenspace.de
Fotos vom letzten Jahr: Google Fotos
Twitter-Hashtag: #devspace

Ansprache von Torsten Weber Ansprache von Torsten Weber

Themenfindung Themenfindung

Diskussion Diskussion

]]>
https://mspi.es/blog/Developer-OpenSpace-in-Leipzig 2015-10-11-16-40 Sun, 11 Oct 2015 16:40:00 GMT
<![CDATA[Mein Fachartikel in der dotnetpro: Von GitHub über Docker Hub über Tutum nach Azure]]> Mein zweiter Fachartikel ist am 17.09. in der Zeitschrift dotnetpro (Ausgabe 10/2015) am Kiosk erschienen.


dotnetpro-Artikel - Von GitHub über Docker Hub über Tutum nach Azure


Im Artikel zeige ich, wie einfach es ist, mit Hilfe von Docker und einigen Cloud-Diensten einen vollautomatisierten Deployment-Prozess einer ASP.NET vNext-Anwendung zu erstellen. GitHub, Docker Hub und Tutum spielen dabei perfekt zusammen.

Ich hoffe, dass ich somit einigen Entwicklern oder sogar Admins den Impuls geben konnte, sich mehr mit Container-Tools, wie Docker zu beschäftigen. Nicht zuletzt, weil es nun auch im Microsoft-Umfeld mit der Einführung von Containern in den Windows Server Einzug hält und somit auf vielen wichtigen Server-Plattformen vertreten ist.

Nun, los zum Kiosk und viel Spaß beim Lesen. ;-)

]]>
https://mspi.es/blog/Mein-Fachartikel-in-der-dotnetpro-Von-GitHub-uber-Docker-Hub-uber-Tutum-nach-Azure 2015-09-26-14-40 Sat, 26 Sep 2015 14:40:00 GMT
<![CDATA[Mein Fachartikel in der dotnetpro: ECMAScript 6 vs. C#]]> Lange Zeit hatte ich es mir schon vorgenommen, doch dann ging alles schneller als ich mir gedacht habe.

Mein erster Fachartikel erscheint am 18.06. in der Zeitschrift dotnetpro (Ausgabe 07/2015) am Kiosk.


dotnetpro-Artikel - ECMAScript 6 vs. C#


Der Artikel vergleicht die Sprachsyntax der neuen ECMAScript 6-Features mit C#. Ich hatte darüber schon vor einiger Zeit gebloggt. Der Artikel behandelt das Thema jedoch noch ein gutes Stück ausführlicher und fokussiert sich deutlicher auf C#. Außerdem wird im Artikel die finale Syntax der Sprachen verwendet, da beim Schreiben des Blogartikels noch nicht alle endgültigen Features feststanden.

Ich freue mich, dass ich durch die Veröffentlichung die Möglichkeit habe mehr Leute zu erreichen und vielleicht den ein oder anderen davon überzeugen kann, dass JavaScript weder der "böse Teufel" noch das "quänglige Kleinkind" ist, sondern ganz oben bei den modernen Sprachen mitspielen kann.

Also, ab zum Kiosk, falls du nicht eh schon ein Abo der dotnetpro besitzt. ;-)

Stay tuned!

]]>
https://mspi.es/blog/Mein-Fachartikel-in-der-dotnetpro-ECMAScript-6-vs.-C 2015-06-16-13-00 Tue, 16 Jun 2015 13:00:00 GMT
<![CDATA[A comparison between the new ECMAScript 6 language features and C#]]> A while ago, I took a look on the new language features of ECMAScript 6, the next JavaScript standard. After a short time, while playing around with the new features, something crossed my mind.

I know a few developers who still have a prejudice against JavaScript. Why shouldn't I try to compare its language features with C# 5.0?! If only one developer gets involved into that nice, lightweight and powerful language after reading this article, I'm quite happy. So, please don't see this blog post as a competition. It really is just a comparison. As of their paradigm and historical background, both languages are quite different.

JavaScript, a dynamic, prototype-based and functional language, has its roots in Lisp. C# on the other side is a static and object-oriented language that looks and works like any other C-based language.

Currently ECMAScript 6 only exists as a draft. It will be standardized in the middle of the next year. But with the transpiler Traceur most of the features can be used today. Keep in mind: nothing is production ready right now.

I've created a short list of some important links, if you want to dive into ECMAScript 6 now.

The comparison

In the following paragraphs there is a short code snippet for the most important and best specified ECMAScript 6 language features. Next to each snippet, you see how to reach the same result (in some cases nearly) with C#. The one comparison or another might be a bit unfair due to the different dynamic and static type system of both languages. If you want to get more information about a feature, then click on the provided "Read more" link at the end of each paragraph.

The JavaScript samples below should run with the great transpiler Traceur released by Google. It allows you to run ECMAScript 6 code in your browser today.

The C# samples can be executed with scriptcs, a scripting environment for C#. Here's an explanation by Scott Hanselman.

Classes

In some cases the concept of classes and inheritance could also make sense in dynamic languages like JavaScript. There are many different frameworks and libraries with their own class system implementation (e.g. Simple JavaScript Inheritance). But every implementation looks a bit different.

In ECMAScript 6 we get a standardized way for this work. Classes, constructors, properties and methods can be defined easily. In addition there's a simple inheritance system. The syntax itself is quite similar to Java.

Read more: http://wiki.ecmascript.org/doku.php?id=strawman:maximally_minimal_classes

JavaScript

class Animal {
  constructor(sound) { this._sound = sound; }
  get sound() { return this._sound; }
  say() { console.log('I ' + this._sound); }
}

class Dog extends Animal {
  constructor(sound) { super('woof'); }
}

class Cat extends Animal {
  constructor(sound) { super('meeeou'); }
}

var dog = new Dog();
dog.say();

var cat = new Cat();
cat.say();

C#

class Animal {
  public Animal(string sound) { Sound = sound; }
  public string Sound { get; private set; }
  public void Say() { Console.WriteLine("I " + Sound); }
}

class Dog : Animal {
  public Dog() : base("woof") { }
}

class Cat : Animal {
  public Cat() : base("meeeou") { }
}

var dog = new Dog();
dog.Say();

var cat = new Cat();
cat.Say();

Modules

In the past, maybe the primitive times, JavaScript code was used as short snippets on static web pages. Nowadays there are large JavaScript applications running in the browser, directly on a client machine or on a server. Because of JavaScripts history, there doesn't exist anything like a module system to divide an application into several parts. You have to modularize your application by using external AMD- or CommonJS frameworks.

With ECMAScript 6 there is a common way to build up your JavaScript application and split it up into manageable parts.

Read more: http://wiki.ecmascript.org/doku.php?id=harmony:modules

JavaScript

// MathLib.js
export function max(...values) { }
export function min(...values) { }
export function avg(...values) { }

// Main.js
import {max, min} from './MathLib'; // relative filename without .js extension

console.log(max(1, 18, 9));
console.log(min(1, 18, 9));

C#

// MathLib/AggregateMethods.cs
namespace MathLib {
  static class AggregateMethods {
    public static int Max(params int[] values) { }
    public static int Min(params int[] values) { }
    public static int Avg(params int[] values) { }
  }
}

// Program.cs
using MathLib;

static class Program {
  public static void Main() {
    Console.WriteLine(AggregateMethods.Max(1, 18, 9));
    Console.WriteLine(AggregateMethods.Min(1, 18, 9));
  }
}

Default values of arguments

There is now the possibility to define optional arguments with some default values. You don't have to blow up your code by handling the arguments object or doing some other technique to do that in JavaScript.

Read more: http://wiki.ecmascript.org/doku.php?id=harmony:parameter_default_values

JavaScript

function splitLine(value, separator = ',') {
  return value.split(separator);
}

console.log(splitLine('value1,value2,value3'));
console.log(splitLine('value1;value2;value3', ';'));

C#

public string[] SplitLine(string value, string separator = ",") {
  return value.Split(new string[] { separator }, StringSplitOptions.None);
}

Console.WriteLine(SplitLine("value1,value2,value3"));
Console.WriteLine(SplitLine("value1;value2;value3", ";"));

Object initializer shorthand

There is no more need to specify redundant field names when objects are created. If you want or have to, you can explicitly define them, as you do it today.

Read more: http://wiki.ecmascript.org/doku.php?id=harmony:object_literals#object_literal_property_shorthands

JavaScript

function getPerson() {
  var name = 'Pete';
  var age = 42;

  return { name, age };
  // Instead of: return { name: name, age: age };
}

console.log(getPerson());
// { name: 'Pete', age: 42 }

C#

public object GetPerson() {
  var Name = "Pete";
  var Age = 42;

  return new { Name, Age };
}

Console.WriteLine(GetPerson());

Lexical block scope (let keyword, const keyword)

There are two new keywords to declare variables. These two don't work like the existing var keyword. So you don't have to struggle with the unintuitive function scope anymore. When the let keyword is used for a variable declaration, it can only be used in that block. In that way it feels more C-ish. const works exactly in the same way as let, but its value cannot be changed after it is initially assigned.

Read more: http://wiki.ecmascript.org/doku.php?id=harmony:block_scoped_bindings

JavaScript

// Sample 1
if(true) {
  let x = 5;
}

console.log(x); // undefined

// Sample 2
var funcs = [];

for(let x=0; x<3; x++) { 
  funcs.push(() => x); 
}

funcs.forEach(f => console.log(f()));
// Output: 0, 1, 2
// when using var instead of let in the for-loop: 3, 3, 3

C#

// Sample 1
if(true) {
  var x = 5;
}

Console.WriteLine(x); // error CS0103: The name 'x' does not exist in the current context

// Sample 2
var funcs = new List<Func<int>>();

for(var i=0; i<3; i++) {
  var value = i;
  funcs.Add(() => value);
}

funcs.ForEach(f => Console.WriteLine(f()));
// Output: 0, 1, 2
// without the value-variable in the for-loop: 3, 3, 3

Concise method definition

In ECMAScript 6 it's tried to reduce the use of the function keyword as often as possible. That's just some syntactic sugar to get less code and doesn't change any logic. The ECMAScript code cannot be really mapped to C#. In C# it's not allowed to access dynamically generated properties by the object initializer from function expressions.

Read more: http://wiki.ecmascript.org/doku.php?id=harmony:concise_object_literal_extensions

JavaScript

var person = {
  name: 'Pete',
  say() { console.log('Hi! My name is ' + this.name); }
}

person.say();

C#

var person = new {
  Name = "Pete",
  Say = new Action(() => Console.WriteLine("Hi! My name is " + this.Name))
};

person.Say();
// error CS0027: Keyword 'this' is not available in the current context

Lambda operator

Yeah, finally we get the lambda (aka fat arrow) operator. There's not much to say about it, but it dramatically reduces the amount of code you have to write. Imagine how your JavaScript code looks like when you use this syntax with something like Linq.js. Very familiar, or not?

Read more: http://wiki.ecmascript.org/doku.php?id=harmony:arrow_function_syntax

JavaScript

var method = () => console.log('No parameters.');
var method2 = x => console.log(`One parameter: x=${x}`);
var method3 = (x, y) => console.log(`Two parameters: x=${x}, y=${y}`);

method();
method2(1);
method3(2, 3);

C#

Action method = () => Console.WriteLine("No parameters.");
Action<int> method2 = x => Console.WriteLine("One parameter: x={0}", x);
Action<int, int> method3 = (x, y) => Console.WriteLine("Two parameters: x={0}, y={1}", x, y);

method();
method2(1);
method3(2, 3);

Rest parameters

Rest parameters exist in C# since the beginning and can be defined with the help of the params keyword. In the current version of JavaScript you have to write a piece of code to get this behavior. In ECMAScript 6 the prepending ... transforms the several arguments outside of the function to an array of values inside of the function. Because JavaScript is dynamic the type of each argument can be different.

Read more: http://wiki.ecmascript.org/doku.php?id=harmony:rest_parameters

JavaScript

function average(...values) {
  return values.reduce((a, b) => a + b) / values.length;
}

console.log(average(1, 1, 3));
// 1.6666666666666667

C#

double Average(params double[] values) {
  return values.Aggregate((a, b) => a + b) / values.Length;
}

Console.WriteLine(Average(1, 1, 3));
// 1.6666666666666667

Spread operator

The spread operator allows to unpack an array into its elements. It's the counterpart of the rest parameter described above. So you can easily create new arrays out of existing arrays. That might be useful in some places. There doesn't exist something similar in C#.

Read more: http://wiki.ecmascript.org/doku.php?id=harmony:spread

JavaScript

// Sample 1
Array.prototype.pushMany = function(itemArrayToAdd) {
  this.push(...itemArrayToAdd);
};

var sports = [];
sports.pushMany(['Soccer', 'Tennis', 'Climbing']);
console.log(sports);
// ['Soccer', 'Tennis', 'Climbing']

// Sample 2
var a = [3, 4];
var b = [7];
var all = [0, 1, 2, ...a, 5, 6, ...b, 8, 9];

console.log(all);
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

C#

// There's nothing similar.

Array comprehensions

UPDATE: Array comprehensions were moved to ECMAScript 7. Get more info in the Harmony working draft revision 27 of August 24, 2014.

In ECMAScript 6 array comprehensions help to initialize arrays in a more convenient way. In C# we have to use some API and don't get any syntactical support to achieve this.

Read more: http://wiki.ecmascript.org/doku.php?id=harmony:array_comprehensions

JavaScript

var value1 = [for (i of [1, 2, 3]) i * i];
var value2 = [for (x of [0, 1, 2]) for (y of [0, 1, 2]) x + '' + y];

console.log(value1); // [1, 4, 9]
console.log(value2); // ['00', '01', '02', '10', '11', '12', '20', '21', '22']

C#

// as LINQ Method Syntax
var value1 = new[] { 1, 2, 3 }.Select(i => i * i);
var value2 = new[] { 0, 1, 2 }.SelectMany(x => new[] { 0, 1, 2 }, (x, y) => x.ToString() + y);

// as LINQ Query Syntax
var value1 = from i in new[] { 1, 2, 3 } select i * i;
var value2 = from x in new[] { 0, 1, 2 } from y in new[] { 0, 1, 2 } select x.ToString() + y;

Console.WriteLine(value1); // [1, 4, 9]
Console.WriteLine(value2); // ['00', '01', '02', '10', '11', '12', '20', '21', '22']

Destructuring assignment

The destructuring assignment is great if a function returns multiple values as an array and these values should be assigned to some individual variables in one step. Or there is a need to extract several field values of a complex object. The syntactical element to achieve this exists in many functional languages. Now we'll get it with ECMAScript 6, too. Please take a look at the link below, because it's a really mighty feature and I only show a few use cases. In C# there doesn't exist something similar.

Read more: http://wiki.ecmascript.org/doku.php?id=harmony:destructuring

JavaScript

// Sample 1
var [m, d, y] = [3, 14, 1977];

console.log(m, d, y);
// 3, 14, 1977

// Sample 2
var x = 3; var y = 4;
[x, y] = [y, x];

console.log(x, y);
// 4, 3

// Sample 3
var person = { name: 'Pete', age: 42 };
var { name: nameOfPerson, age: ageOfPerson } = person;

console.log(nameOfPerson);
// Pete
console.log(ageOfPerson);
// 42

C#

// There's nothing similar

Binary and octal literals

Now there is a native way to describe binary and octal values. In C# there are just some special identifiers for the literals float, double and decimal. Normally the type byte is used to describe data.

JavaScript

console.log(0b0,  0b1, 0b11);
// 0, 1, 3

console.log(0o0, 0o1, 0o10, 0o77);
// 0, 1, 8, 63

C#

// There's nothing similar.

Generator functions

Generators functions produce return values, which can be iterated by a for-loop. Note that they are evaluated by the new special loop syntax for(var x of values). The method is only executed until the next yield statement is reached. C# also contains that kind of functions. Because it has a static type system the return value has to be of type IEnumerable or IEnumerable<T>.

Read more: http://wiki.ecmascript.org/doku.php?id=harmony:generators

JavaScript

function* myGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

for(var value of myGenerator()) {
  console.log(value);
}

C#

 IEnumerable MyGenerator() {
  yield return 1;
  yield return 2;
  yield return 3;
}

foreach(var value in MyGenerator()) {
  Console.WriteLine(value);
}

Promises

There are some popular JavaScript frameworks like jQuery or AngularJS, which provide a simple API that allows to define chainable asynchronous workflows. In ECMAScript 6, this API is part of the base library. In C# we can use the TPL with its continuation tasks.

Read more: http://www.html5rocks.com/en/tutorials/es6/promises/

JavaScript

function timeout(inMilliseconds) {
  return new Promise((resolve) => {
    console.log('start promise...');

    setTimeout(function() { resolve('returnValueOfTheTimeoutMethod'); }, inMilliseconds);
  });
}

timeout(1000)
  .then(() => console.log('... finished promise'))
  .then(() => timeout(1000))
  .then(x => console.log(`... another time with the return value ${x}`));

C#

string Timeout(int inMilliseconds)
{
  Console.WriteLine("start task...");
  Thread.Sleep(inMilliseconds);
  return "returnValueOfTheTimeoutMethod";
}

var task = new Task(() => Timeout(1000));

task
  .ContinueWith(_ => Console.WriteLine("...finished task"))
  .ContinueWith(_ => Timeout(1000))
  .ContinueWith(t => Console.WriteLine("... another time with the return value {0}", t.Result));

task.Start();

Console.ReadLine();

Collections (sets, maps, weak maps)

ECMAScript 6 also specifies a few new objects to handle special sorts of lists. There exist classes to work with maps (in C# Hashtable or Dictionary<T>), sets (in C# HashSet<T>) or weak maps.

Read more:

JavaScript

var map = new Map();
var uniqueItems = new Set();
var weakMap = new WeakMap();

C#

var map = new Hashtable();
var uniqueItems = new HashSet<object>();
// There's nothing similar for a WeakMap. A custom implementation of a Dictionary<WeakReference, object> is needed.

Template strings

Template strings are just awesome. If the single quotes to define a string are exchanged with backticks, then you can easily insert the value of a variable in it. Backticks might seem unfamiliar to C# developers, but they exist in many other languages out there. Template strings are a bit like C#s string.Format() on steroids.

Read more: http://wiki.ecmascript.org/doku.php?id=harmony:quasis

JavaScript

var person = { name: 'Pete', age: 42};

console.log(`Hello ${person.name}, you are ${person.age} years old!`);
// Hello Pete, you are 42 years old!

C#

var person = new { Name = "Pete", Age = 42};

Console.WriteLine("Hello {0}, you are {1} years old!", person.Name, person.Age);
// Hello Pete, you are 42 years old!

Proxy

Furthermore there is the introduction of a proxy object. That object can be placed around some existing object. Then the property access of the source object can be intercepted. To gain that functionality in C#, there are several options. The use of C# Emit stuff to generate code on the fly or existing libraries, like Castle DynamicProxy. Although the same can be done with frameworks like PostSharp or Fody by using IL weaving.

Read more: http://wiki.ecmascript.org/doku.php?id=harmony:direct_proxies

JavaScript

// Sample 1
var person = { name: 'Pete', age: 42 };
var proxiedPerson = new Proxy(person, {});

proxiedPerson.age = 37;

console.log(person.a); // 37
console.log(proxiedPerson.a); // 37

// Sample 2
var person = { name: 'Pete', age: 42 };

let validator = {
  get: function(obj, prop) {
    return obj[prop];
  },

  set: function(obj, prop, value) {
    if (prop === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError('The age is not an integer');
      }

      obj[prop] = value;
    }
  }
};

var proxiedPerson = new Proxy(person, validator);

proxiedPerson.age = 37;     // Okay
proxiedPerson.age = 'test'; // Error

C#

// No native or easy implementation
// Take a look at Castle DynamicProxy or similar libraries

Conclusion

As you could see, JavaScript really, really caught up to modern programming languages. Not only the speed of the language was improved over the last years. With the new ECMAScript 6 features like modules and classes, the way is paved for larger applications. All other features will bring many small improvements to your source code or will reduce its amount.

]]>
https://mspi.es/blog/A-comparison-between-the-new-ECMAScript-6-language-features-and-C 2014-08-26-22-15 Tue, 26 Aug 2014 22:15:00 GMT
<![CDATA[Wie du dein eigenes NAS-System aufsetzt - Die ultimative Anleitung]]> Ich habe einen riesigen Haufen an Daten. Mehr oder weniger sinnvolle Medien, wie Filme, Musik, Urlaubs-Fotos, etc., aber auch System-Backups mehrerer PCs, Source Code oder sonstige über die Jahre angesammelten Dokumente. Jetzt könnte man mir sagen "Lösch doch den alten Mist!". Als Digital-Messi des 21. Jahrhunderts kann ich auf diesen Vorschlag natürlich nicht hören.

Was ich wirklich brauche ist ein System, das mir Schutz gegen Plattenausfälle bietet, als Drive-Pool arbeiten kann, mir optional eine Verschlüsselung bietet und meine Daten über einen Netzwerkshare nach außen zur Verfügung stellt. Geld für teure Spezialhardware, wie ein QNap, will ich selbstverständlich auch nicht ausgeben.

Einen Rechner, der als Server herhalten kann, steht bei mir rum. Zwei neue 3 TB-Platten musste ich noch besorgen und einbauen. Die Hardware ist also bereit. Nun fehlt noch das Software-NAS-System.

Disclaimer: Ich stelle hier einige Tools vor, mit welchen sich ein NAS-System mit RAID 5 oder RAID 6 einfach umsetzen lässt. Es geht also nicht um Hardware, Rumgeschraube, Stromverbrauch oder ähnliches, sondern rein um einen softwareseitigen Ansatz. Außerdem gehe ich davon aus, dass du ein paar Linux-Basics mitbringst.

So, los gehts. Nun bist du dran.

System

Als Grundsystem empfehle ich dir ein Ubuntu Server 14.04 LTS zu nehmen. Grundsätzlich ist die Distribution aber egal. Ich habe mich wegen der hohen Verbreitung für ein Debian-basiertes System (GNU/Linux Distribution Timeline) und wegen der Benutzerfreundlichkeit zu Ubuntu entschieden. Von daher ist das HowTo auch auf dieser Grundlage entstanden.

Ein Tutorial für die Installation von Ubuntu Server findest du auf ubuntuserverguide.com. Achte bei der Installation darauf, dass die Systemplatte, also die Festplatte auf die du dein Linux-OS installierst, nicht vom RAID-System verwendet werden sollte. So bekommst du eine saubere Trennung zwischen deinem OS und NAS-System hin.

Vorbereitung der Festplatten

Bevor du Dateien und Ordner auf deinen Festplatten ablegen kannst, müssen natürlich noch einige Dinge vorbereitet werden. Das alte Spiel:

  1. Partitionieren, um die physikalische Platte in logische Einheiten aufzuteilen
  2. Formatieren mit einem Dateisystem
  3. Mounten im OS, damit du darauf zugreifen kannst

Partitionierung

Installiere zuerst gparted. Dieses Tool besitzt GPT (Guid Partition Table)-Support und hat somit gegenüber von fdisk den Vorteil, dass sich Partitionen jenseits der 2 TB anlegen lassen. Platz verschenken will ja niemand.

Installation

$ sudo apt-get install parted

Logische Bezeichnung der Festplatten herausfinden

Wenn du nicht auf russisches Roulette stehst, dann rate ich dir vor dem eigentlichen Partitionieren erst einmal den logischen Namen der Festplatte, die du Partitionieren willst herauszufinden. Normalerweise ist er folgendermaßen aufgebaut: /dev/sda, /dev/sdb, /dev/sdc, usw.

Das Kommando sudo lshw -C disk hilft dir dabei (mehr Infos):

*-disk:0
       description: ATA Disk
       product: ST3000VN000-1H41       <--- Typ
       vendor: Seagate                 <--- Hersteller
       physical id: 0.0.0
       bus info: scsi@2:0.0.0
       logical name: /dev/sdb          <--- logischer Name
       version: SC43
       serial: S300BVEX
       size: 2794GiB (3TB)
       capacity: 2794GiB (3TB)

Wenn mehrere Festplatten vorhanden sind, dann tauchen in der Ausgabe auch mehrere dieser Abschnitte auf. Merke dir den Eintrag logical name. Ich werde um dieses HowTo verständlich zu halten ab jetzt immer /dev/sdb als Platzhalter verwenden. Setze bei dir dafür den richtigen Namen ein.

Partition erstellen

$ sudo parted /dev/sdb
(parted) mklabel gpt
(parted) unit TB
(parted) mkpart primary 0 -1

Wenn du nun innerhalb der parted-Shell das Kommando print eingibst, dann solltest du in etwa das Folgende auf dem Bildschirm sehen. Wichtig ist die Stelle Partition Table: gpt und dass exakt eine Partition erstellt wurde, die die Größe der Festplatte besitzt (letzte Zeile).

(parted) print
Model: ATA ST3000VN000-1H41 (scsi)
Disk /dev/sdb: 3001GB
Sector size (logical/physical): 512B/4096B
Partition Table: gpt

Number  Start   End     Size    File system  Name     Flags
 1      1049kB  3001GB  3001GB  ext4         primary

Mit der Taste q kannst du parted wieder verlassen und mit dem nächsten Schritt weitermachen.

Wahl des Dateisystems

Bei Ubuntu Server 14.04 (Trusty Tahr) wird standardmäßig ext4 als Dateisystem verwendet. Google selbst hat vor einigen Jahren seine komplette Infrastruktur von ext2 auf ext4 migriert. Ext4 ist ein Journaling-Dateisystem, das eine große und für dein NAS-System auf jeden Fall außreichende Menge an Features bietet. Außerdem muss SnapRAID (Beschreibung kommt weiter unten im HowTo) dieses Dateisystem auch unterstützen. Ext4 wird in der SnapRAID-FAQ auch empfohlen kann daher unengeschränkt genommen werden.

Leider war Btrfs zum Zeitpunkt der Einrichtung noch nicht offiziell released. Btrfs unterstützt unter anderem nativ Drive-Pools, Snapshotting und mehr. Eine spätere Konvertierung kannst du aber Dank offiziellem in-place conversion Support problemlos machen.

Partition formatieren

Gib einfach folgenden Befehl ein um die erste und einzige Partition der Festplatte /dev/sdb mit dem Dateisystem ext4 zu formatieren. Die 1 hinter /dev/sdb steht für die Partitionsnummer.

$ sudo mkfs.ext4 /dev/sdb1

Erstellung der Mount-Points

Alles klar, die erste Festplatte ist nun fast fertig vorbereitet. Nun musst du nur noch dafür sorgen, dass sie bei Betriebsystemstart automatisch gemountet wird. Und dafür solltest du nicht mehr den logischen Namen /dev/sdb1, sondern lieber einen UUID verwenden. Wie du siehst sind die UUIDs wesentlich komplizierter zu merken als der logische Name. Dafür haben sie aber einen gewaltigen Vorteil. UUIDs bleiben für Partitionen immer gleich und eindeutig. Angenommen du hängst eine Festplatte an einen anderen Controller oder Port. Dann kannst du dich schon nicht mehr auf den logischen Namen verlassen, auf die UUID schon.

Auflisten der UUIDs

Mit dem folgenen Kommando bekommst du eine Liste der Partitionen, inkl. ihrer UUIDs.

$ sudo blkid

Nun "merke" dir die UUID 778f5de6-dcf8-4d3d-b52b-3ac93a6dbb2d (hier im Beispiel) zur Partition /dev/sdb1. Bei dir steht hier natürlich eine andere UUID oder Partition.

/dev/sda1: UUID="559be6c6-68f0-469e-8fce-4f09094b6a88" TYPE="ext2"
/dev/sda5: UUID="A7XJ1x-cZCe-kEc0-Ls39-f98d-UQNm-RuzCcj" TYPE="LVM2_member"
/dev/mapper/storage--vg-root: UUID="5edd9bf8-b3fd-49fa-8595-42d099a9a44a" TYPE="ext4"
/dev/mapper/storage--vg-swap_1: UUID="4b473d87-b99f-4b2c-8f2d-d82dff8bd364" TYPE="swap"
/dev/sdc1: UUID="4cc5b927-7e45-4197-9783-28c8dcbb745f" TYPE="ext4"
/dev/sdd1: UUID="8b817bb0-068e-4873-b3a4-8afc47b53059" TYPE="ext4"
/dev/sdb1: UUID="778f5de6-dcf8-4d3d-b52b-3ac93a6dbb2d" TYPE="ext4"

Eintragen in /etc/fstab

Deine Partition soll nun in das Verzeichnis /mnt/data1 gemountet werden. Damit das automatisch bei Betriebssystemstart geschieht, musst du in der Datei /etc/fstab einen Eintrag hinzufügen.

$ sudo mkdir /mnt/data1
$ sudo vi /etc/fstab

Am Ende musst du diese Zeile mit der von dir gemerkten UUID und dem Mount-Pfad anhängen.

...

UUID=778f5de6-dcf8-4d3d-b52b-3ac93a6dbb2d  /mnt/data1  ext4  defaults  0  2

Mounten und überprüfen

Ob alles funktioniert hat, siehst du, wenn du den hinzugefügten Mount-Eintrag von Hand lädst und dir danach die gemounteten Partitionen ausgeben lässt.

$ sudo mount -a
$ mount

In der Ausgabe sollte irgendwo deine Partition in Verbindung mit dem Mount-Pfad zu finden sein. Im Beispiel unten ist das die markierte Zeile. Die UUID taucht hier nicht mehr auf.

...

none on /sys/fs/pstore type pstore (rw)
/dev/sda1 on /boot type ext2 (rw)
/dev/sdb1 on /mnt/data1 type ext4 (rw)          <--- Das ist deine gemountete Partition
systemd on /sys/fs/cgroup/systemd type cgroup (rw,noexec,nosuid,nodev,none,name=systemd)
none on /mnt/pool type aufs (rw,relatime,si=bbe4bb5e4ecd5450,create=mfs,sum)

...

Software-RAID

Du musst für jede neue Platte, die in in den RAID-Verbund soll die Schritte von oben durchführen. Partitionieren und Formatieren musst du natürlich nicht, falls die Festplatte schon ein unterstützes Dateisystem hat und du die Daten darauf behalten willst.

Wenn du damit fertig bist, dann geht's mit der Installation und Konfiguration von SnapRAID weiter. Dieses Tool bringt deine Partitionen in einen RAID 5- oder RAID 6-Verbund.

Etwas genauer: Es erstellt eine oder mehrere Dateien mit Parity-Informationen der Platten aus dem Verbund. Wenn nun eine Platte crasht, dann tauschst du sie einfach gegen eine funktionierende leere aus und stellst die Daten mithilfe von SnapRAID wieder her. Eine Parity-Datei wächst auf die gleiche Größe, wie die größte Festplatte im Verbund und kann eine ausgefallene Festplatte wiederherstellen. Willst du mehr Sicherheit, dann kannst du mit zwei Parity-Dateien bis zu zwei gleichzeitig ausgefallene Festplatten wiederherstellen. Genial!

Der SnapRAID-Author empfiehlt in seinem FAQ für die ersten vier Laufwerke ein Parity und für alle folgenden Gruppen von ca. sieben Laufwerken ein weiteres Parity zu verwenden.

Parities Daten-Laufwerke
1 / Single RAID (RAID 5) 2-4
2 / Double RAID (RAID 6) 5-14
3 / Triple RAID 15-21
4 / Quad RAID 22-28
5 / Penta RAID 29-35
6 / Hexa RAID 36-42

[Quelle: SnapRAID-FAQ]

SnapRAID schreibt die Parity-Informationen nicht on-the-fly. Das bedeutet, dass deine Daten eventuell veraltet sein könnten, wenn du sie wieder herstellen musst. Der Vorteil ist, dass Daten schnell und ohne Verzögerung geschrieben werden. Und du kannst Daten wiederherstellen, die du versehenlich zerstört oder gelöscht hast. Das kann schnell passieren: fehlerhafte Software, falscher Befehl auf der Kommandozeile, nervöser Finger auf der Maus, ein paar Bier zuviel beim Filmabend, usw.

Mir waren die Vorteile wichtiger als der Nachteil. Wenn die Parity-Infos einmal in der Nacht erneuert werden, dann ist das für mich mehr als ausreichend. Du kannst die Parity-Informationen natürlich auch jede Stunde erzeugen lassen, wenn dich das ruhiger schlafen lässt.

Okay, genug theoretisches Bla. Jetzt wird installiert und eingerichtet. Dich in den nächsten Beispielen davon aus, dass du drei Festplatten im RAID 5 verwenden willst.

  • /mnt/parity (3 TB) <--- Platzbedarf der Parity-Infos ist immer so hoch, wie die größte Platte im Verbund
  • /mnt/data1 (3 TB)
  • /mnt/data2 (2 TB)

Package-Repository hinzufügen und installieren

SnapRAID gibt es nicht im offiziellen Ubuntu-Repository. Bei Launchpad hostet der SnapRAID-Entwickler immer die aktuellen Pakete. Du musst jetzt nur noch mit dem Befehl add-apt repository das SnapRAID-Launchpad-Repository hinzufügen, die Package-Liste aktualisieren und SnapRAID installieren.

$ sudo add-apt-repository ppa:tikhonov/snapraid
$ sudo apt-get update
$ sudo apt-get install snapraid

Konfigurieren von SnapRAID

Nach dem Öffnen der Konfigurationsdatei von SnapRAID...

$ vi /etc/snapraid.conf

... musst du nur noch drei Dinge konfigurieren:

  1. das Ziel für deine Parity-Datei
    Auf der Partition, wo diese Datei liegt, brauchst du soviel freien Platz, wie die größte deiner Daten-Festplatten hergibt.

  2. mindestens eine Content-Datei
    Hier liegen die Checksums zu all deinen Daten. Mit ihrer Hilfe und den Parity-Informationen kann SnapRAID deine Daten wiederherstellen. Daher ist es Pflicht auf jeder Platte in deinem RAID-Verbund + einmal zusätzlich auf einer anderen Platte (z.B. die Systempartition) die Datei abzulegen. Bei mir fällt die Content-Datei mit 280 MB für 4,5 TB Daten nicht wirklich ins Gewicht. Von den Content-Files werden keine Parity-Infos gemacht.

  3. die Mount-Punkte zu deinen Daten-Platten
    Hier landen deine eigentlichen Daten. Aus diesen werden die Parity-Informationen berechnet. Du kannst den Mount-Punkt der Daten beliebig anpassen. SnapRAID verknüpft nur den Identifier (im Snippet unten d1 und d2) mit den Parity-Informationen und der Content-Datei.

  4. die exclude-Direktiven sind optional
    Diese Dateien werden von der Parity-Berechnung ausgeschlossen.

Mehr Einstellungen und Details gibt's im SnapRAID Manual.

So, genug der Theorie! Hier ist die Beispielkonfiguration.

# 1. Die Parity-Informationen
parity /mnt/parity/snapraid.parity

# 2. Informationen über die abgelegten Dateien (Checksums)
content /var/snapraid/content
content /mnt/data1/snapraid.content
content /mnt/data2/snapraid.content

# 3. Die Partitionen mit den Daten
disk d1 /mnt/data1/
disk d2 /mnt/data2/

# 4. Von den Parity-Informationen ausgeschlossene Dateien (optional)
exclude *.unrecoverable
exclude /tmp/
exclude /lost+found/
exclude .DS_Store
exclude .Thumbs.db

Auch wenn du schon Dateien auf deine Daten-Platten draufkopiert hast, hat SnapRAID noch kein Prozent an CPU-Leistung verbraten. Deshalb bist du auch noch nicht vor einem Ausfall geschützt. SnapRAID läuft nämlich nicht als Daemon.

Um die Parity-Informationen zu erzeugen musst du in der Kommandozeile nur den folgenden Befehl eingeben. Keine Angst, das musst du nicht jede Nacht oder jede Stunde tun um sicher zu sein. Ich zeige dir etwas weiter unten wie das automatisch geht.

$ sudo snapraid sync

Wenn du schon einige Daten auf deinen Platten hast, dann kannst du dir erstmal ein paar Bier schnappen und mehrere Stunden gespannt auf die Fortschrittsanzeige starren.

Nach dem Sync-Vorgang bist du gegen den Ausfall einer deiner Platten sicher geschützt. Jedesmal wenn du Daten änderst oder hinzufügst, musst du die Parity-Informationen für diese Änderungen neu erstellen. Das bedeutet, dass bei deinen nächsten sync-Aufrufen SnapRAID nur noch die Parity-Infos für die geänderten Daten berechnet und von daher schneller als beim ersten Mal durchläuft.

Cronjob für Parity-Informationen

Du willst dich bestimmt nicht ständig hinhocken, per SSH einloggen und die Parity-Informationen von Hand erstellen. Das endet damit, dass du es mal vergisst, Fehler machst oder aufgrund notorischer Faulheit ignorierst ("Ach, wird heute schon nix passieren."). Außerdem ist deine Zeit für sowas auch zu Schade. Das heißt, dass es für dich nur einen Ausweg aus der Misere gibt: Automatisierung.

Ein Script für den sync-Vorgang hat glücklicherweise schon jemand geschrieben. Du findest es hier.

Warum ein Script, wenn du doch eigentlich nur einen Befehl brauchst? Ganz einfach. Selbstschutz, Sicherheit und Transparenz. Das Script geht mehrstufig vor:

  1. es schaut zuerst nach Änderungen (snapraid diff)
  2. wenn nun viele Dateien gelöscht worden sind (Einstellbar mit der Variable DEL_THRESHOLD) dann wird kein sync-Vorgang ausgeführt. Es könnte ja sein, dass du versehentlich einen Ordner mit Daten gelöscht hast. Oder es hat sich eine deiner Platten verabschiedet, ohne dass es dir aufgefallen ist.
  3. dir wird eine Mail mit dem Ergebnis zugestellt

Passe in dem Script einfach ein paar Variablen an (EMAIL_ADDRESS, CONTENT_FILE, PARITY_FILE, DEL_THRESHOLD) und kopiere es in das Verzeichnis /etc/cron.daily. Schon wird es einmal am Tag ausgeführt. Überfliege den Code einfach kurz. Dann weißt du Bescheid was genau passiert. Es sind nur ein paar Zeilen.

SnapRAID freut sich im übrigen, wenn es pro 10 TB Daten ca. 1 GB RAM bekommt. Wenn du zu wenig RAM zu Verfügung hast, dann schau dir mal den blocksize-Parameter im Manual an.

Verschlüsselung

Um die Verschlüsselung der Daten habe ich mich bisher noch nicht gekümmert. Eine mögliche Umsetzung wäre LUKS (Linux Unified Key Setup). Bei dieser Verschlüsselung wird das komplette Device /dev/sdb verschlüsselt. Die Entschlüsselung geschieht also während dem mounten und liegt transparent eine Ebene unterhalb von SnapRAID. Somit kannst du sogar theoretisch nur einzelne Festplatten aus deinem Verbund verschlüsseln. Davon rate ich dir allerdings ab. Wirklich sicher ist es nur, wenn du alle Daten-Festplatten, inklusive der Parity-Platte verschlüsselst.

Hier ist ein kurzes Tutorial das die Einrichtung von LUKS zeigt: http://www.cyberciti.biz/hardware/howto-linux-hard-disk-encryption-with-luks-cryptsetup-command/

Mount-Pool

Das Meiste ist jetzt geschafft und du bist kurz vorm Ziel.

Was ist nun, wenn du, so wie ich, mit großen Datenmengen zu kämpfen hast und der Speicherplatz einer deiner Festplatten zu eng wird. Dann liegt am Ende ein Teil der Daten, die eigentlich zusammen gehören sollten, auf /dev/data1 und ein Teil auf /mnt/data2. Oder du musst deine Daten mühsam von Hand so umkopieren, dass es passt. Sowas will niemand.

Unter Linux gibt's dafür ein elegantes und leichtgewichtiges Mittel. Das virtuelle Dateisystem Aufs. Es macht eigentlich nichts anderes als verschiedene Mountpoints zusammenzufassen und nach außen als einen großen Pool sichtbar zu machen. Beim Schreiben von Daten gewinnt die Festplatte mit der meisten freien Kapazität. Die kleinste Einheit ist eine Datei. Das heißt Dateien-Fragmente werden nicht über verschiedene Festplatten verteilt. Bei Ordnerstrukturen kann dir das aber schon passieren. Das macht aber nichts und sollte dich auch nicht weiter stören. Du betrachtest deine Daten einfach immer durch den Pool und bekommst sie so auch immer zusammen angezeigt.

Was ist nun dein Vortei? Geht dir der Platz aus, dann baust du dir einfach eine neue Platte ein und fügt sie in der Aufs-Konfiguration hinzu. That's it. Der Pool vergrößert sich dann automatisch. Deine Ordner-Strukturen behältst du einfach so bei wie sie sind. Der Pool ist eine große "Partition" die wachsen kann, wenn du mehr brauchst.

Installation der Aufs-Tools

Zuerst musst du das benötigte Paket installieren. Es ist im Ubuntu-Repository enthalten.

$ sudo apt-get install aufs-tools

Mounten bei Systemstart

Erstelle nun einen Ordner, in dem dein Mount-Pool später liegen soll. Danach musst du nur noch im Script /etc/rc.local, das automatisch bei jedem Systemstart ausgeführt wird, einen entsprechenden Mount-Befehl hinzufügen. Die Aufs-Tools können nur bereits gemountete Partitionen zusammenfassen. Nimm deswegen dieses Script, weil es erst nach dem Verarbeiten der Datei /etc/fstab ausgeführt wird.

$ sudo mkdir /mnt/pool
$ sudo vi /etc/rc.local

Diese Zeile musst du in das Script eingefügen, um die beiden Partitionen /mnt/data1 und /mnt/data2 zu einem Pool zusammenzufassen.

mount -t aufs -o br:/mnt/data1=rw:/mnt/data2=rw,sum,create=mfs none /mnt/pool

Führe jetzt noch einen sudo reboot durch und schaue, ob alles sauber hochfährt. In Zukunft brauchst du dich weder für /mnt/data1, /dev/sdb, /dev/sdb1 noch /mnt/parity zu interessieren. Du arbeitest einfach immer auf deinem Pool /dev/pool. Der Rest passiert automatisch im Hintergrund.

Netzwerkshare für den Zugriff von Windows aus

Um nun dem N aus NAS für Windows-Nutzer noch gerecht zu werden, brauchst du noch einen Samba-Share. Du solltest aus sicherheitstechnischen Gründen immer einen gesonderten Samba-Benutzer verwenden. Dieser Benutzer hat nur Rechte auf deinen Pool. Nicht aber auf das System.

Da es sich bei mir nur um ein Heimnetzwerk handelt habe ich mich nicht groß mit Multiuser-Kram oder speziellen Berechtigungen aufgehalten. Deshalb zeige ich dir nur die Basics. Wenn du mehr wissen willst, dann schaue am Besten in die Samba-Dokumentation.

Samba-Dienst installieren

Der Samba-Dienst ist im offiziellen Ubuntu-Repository enthalten.

$ sudo apt-get install samba

Share-Verzeichnis und Samba-Benutzer erstellen

Erstelle zuerst das Verzeichnis share in deinem Mount-Pool. Das ist das Verzeichnis, dass du gleich freigibst. Lege dann einen Benutzer mit dem Namen samba an und gib ihm ein Passwort. Das Samba-Passwort für den Netzwerkzugriff und das Systempasswort für den Benutzer können voneinander abweichen. Wenn du damit fertig bist, dann gib zum Abschluss dem erstellten Benutzer vollen Zugriff (Owner-Rechte) auf den share-Ordner.

Hier hast du die entsprechenden Kommandos.

$ sudo mkdir /mnt/pool/share
$ sudo adduser samba
$ sudo smbpasswd samba
$ sudo chown samba /mnt/pool/share

Share-Verzeichnis im Netzwerk freigeben

Füge der Datei smb.conf einen von den beiden Abschnitten unten hinzu.

$ sudo vi /etc/samba/smb.conf

Wenn du diesen Abschnitt verwendest, dann brauchst du beim Verbinden zum Share den Samba-Benutzer und das Passwort (s.o. sudo smbpasswd samba).

[share]
  path = /mnt/pool/share
  browseable = yes
  writeable = yes
  valid users = samba
  force user = samba
  create mask = 664
  security mask = 664
  directory mask = 2775

Ist dir die Authentifizierung egal und jeder soll auf deinen Share zugreifen können, dann nimm diese Konfiguration.

[share]
  path = /mnt/pool/share
  browseable = yes
  writeable = yes
  guest ok = yes
  force user = samba
  create mask = 664
  security mask = 664
  directory mask = 2775

Samba-Server neu starten

Jetzt starte den Samba-Server neu, damit deine Änderungen auch wirksam werden.

$ restart smbd

Neue Platte - Was ist zu tun?

Der Konfigurationsaufwand, wenn du eine neue Festplatte als Mitglied in deiner RAID-Familie begrüßen willst, ist IMO sehr überschaubar.

  1. Partitionieren + Formatieren (das muss man eh immer machen)
  2. /etc/fstab anpassen (1 Zeile)
  3. SnapRAID-Konfiguration anpassen (2 Zeilen)
  4. Aufs-Konfiguration anpassen (1 Zeile)

Fertig! Du hast es endgültig geschafft! Dein heiligstes, also deine Daten, ist sicher und du kannst auf einfache Weise drauf zugreifen.

Fazit

Es existieren natürlich auch Komplettsysteme, die alle genannten Punkte beherrschen. Wenn ich mich aber auf so ein System einlasse, dann geht mir die Leichtgewichtigkeit, Austauschbarkeit und Flexibilität verloren. Ich will meine Daten unter keinen Umständen in einer Blackbox halten, sondern wissen was passiert. Vor allem soll so ein System auch die nächsten Jahre halten. Falls ich zwischendurch eine Erweiterung benötige oder einen Teil austauschen will/muss, dann soll das also für mich auch auf jeden Fall möglich sein ohne den ganzen Pool mit einem riesigen Aufwand neu aufzusetzen. Außerdem kann ich mit diesem Konzept im Notfall einfach einzelne Platten austauschen oder auch in andere Rechner einbauen und dort auf die Daten zugreifen.

Windows 8 Storage Spaces oder auch andere Systeme sind ganz nett, aber haben oft den Nachteil, dass die Platten beim Registrieren im Pool komplett gelöscht werden, bzw. außerhalb des Pools im einzelnen nicht mehr verwendet werden können. Mit einem modularen System hast du nun die Freiheit Dateisysteme zu mischen, frisch formatierte oder Festplatten mit Daten zu verwenden. Falls mal ein Problem auftritt, dann kannst du es einfacher identifizieren. Und das Ganze mit einem sehr geringen Konfigurationsaufwand im Verhältnis zu Nutzungsdauer. Die Einrichtung machst du einmal. Das Zugreifen auf die Daten ständig über Jahre hinweg.

Setzt du schon etwas ein? Welche Hardware oder welche Software? Hinterlasse mir einen kurzen Kommentar oder Tweet!

]]>
https://mspi.es/blog/Wie-du-dein-eigenes-NAS-System-aufsetzt-Die-ultimative-Anleitung 2014-06-02-17-00 Mon, 02 Jun 2014 17:00:00 GMT
<![CDATA[Continuous Integration von Node.js-Projekten mit GitHub und Travis-CI]]> Bei Travis-CI handelt es sich um einen Online-Build-Dienst, welcher für OpenSource-Projekte kostenlos ist. Aktuell funktioniert das allerdings nur, wenn sich das Source-Code-Repository auf GitHub befindet. Hier eine aus der Online-Dokumentation stammende Liste der unterstützten Programmiersprachen:

Für Ruby, PHP und Node.js werden mehrere verschiedene Runtime-Versionen angeboten. Alle Einstellungen werden in einer zentralen Konfigurationsdatei .travis.yml mit der leichtgewichtigen Markup-Sprache YAML erstellt.

Der Travis-CI-Code, vom Blog, über die REST-Api, Web-Site, vielem mehr bis zu den Chef-Cookbooks für die Erzeugung der virtuellen Build-Maschinen, gibt es OpenSource auf https://github.com/travis-ci.

Was kann ich out of the box verwenden?

Die Build-Nodes von Travis-CI basieren auf Ubuntu Server 12.04 LTS und haben mindestens eine Version von Ruby, OpenJDK, Python, Node.js und Go installiert. Zudem ist ein Set an Netzwerktools wie wget oder curl verfügbar. Ebenso gibt es make und noch einige weitere für Build-Prozesse hilfreiche Tools. Es existieren auch noch einige Data Stores, wie MySQL, MongoDB, Redis, Neo4J, uvm. Individuell auf Builds reagieren kann man mit Hilfe einer Menge an Umgebungsvariablen, wie zum Beispiel der abgerufene Branch, das Build-Verzeichnis, die Build-Nummer, und vielen mehr.

Alle Details über die CI-Umgebung können hier nachgelesen werden.

Konfiguration eines Node.js-Projekts

Im weiteren Verlauf zeige ich Schritt für Schritt, wie ein Node.js-Projekt mit Travis-CI automatisiert werden kann.

1. Travis-CI-Account erstellen

Gehe auf https://travis-ci.org und melde dich oben rechts in der Ecke mit dem GitHub-Account an, in welchem das zu bauende Repository liegt.

2. Repository aktivieren

Im Profil kann man nun mit einem Mausklick die Repositories auswählen für die Travis-CI aktiviert wird.

Travis-CI Repositories

3. .travis.yml anlegen

Im Grunde reicht es in den meisten Fällen aus die folgende Konfiguration in einer Datei mit dem Namen .travis.yml im Hauptverzeichnis des Repositories abzulegen.

language: node_js
node_js:
  - "0.10"

Hier wird definiert, dass Travis-CI als Runtime das aktuelle Stable-Release 0.10.x von Node.js verwendet.

Node.js Build-Vorgang

Bei jedem Node.js-Build-Vorgang werden zwei bestimmte NPM-Kommandos ausgeführt.

  1. npm install zum Installieren aller für das Projekt notwendigen Abhängigkeiten
  2. npm test zum Ausführen der Unit- und Integration-Tests

Alle weiteren Änderungen können nun direkt in der Datei packages.json eingestellt werden. Die Verarbeitung und Ausführung geschieht über npm, wie man es von Node.js-Projekten gewohnt ist.

...

"scripts": {
  "test": "vows --spec"
},

...

In diesem Beispiel wird nun der Testrunner vows mit dem --spec-Parameter verwendet.

Build Lifecycle

Wenn man mehr Freitheiten benötigt, kann man auch mithilfe von Scripten an jeder notwendigen Stelle des Build Lifecycles eingreifen. Im nächsten Beispiel ist eine Konfiguration abgebildet, welche diese entsprechenden Stellen verwendet.

language: node_js
node_js:
  - "0.10"
before_install: "sudo apt-get update"
install: "sudo apt-get install ..."
before_script: "npm install -g grunt-cli"
script: "npm run-script ci"
after_success:
  - "echo Good! :-)"
  - "echo Everything seems to work!"
after_failure: "echo Bad! :-("
after_script: "echo Finished!"

Der Ablauf der Build Lifecycles ist hier genau beschrieben.

Stateless

Selbstverständlich wird kein State zwischen den Builds gespeichert. Durch eventuelle Seiteneffekte würde man sich die Garantie nehmen, dass alle Automatisierungsmechanismen existieren um die Anwendung erfolgreich erstellen zu können. Die Maschinen werden bei Travis-CI im Hintergrund mittels VM-Snapshotting nach jedem Build wieder zurückgesetzt werden.

4. Commit & Push

Nun musst du die Travis-Konfiguration nur noch committen und auf GitHub pushen. Die Ergebnisse der vergangenen, sowie Live-Konsolenausgaben des aktuellen Builds kannst du nun auf https://travis-ci.org unter dem Tab "My Repositories" verfolgen. Hier die Ausgaben anhand eines Beispiel-Projektes.

Jeder weitere Push löst automatisch einen Build-Vorgang aus. Auch jeder Pull-Request wird überprüft. Die Build-Ergebnisse werden nachdem die Builds durchgelaufen sind automatisch auf GitHub angezeigt.

GitHub Pull-Request CI

Fazit

Wenn du an einem Projekt in einer der oben genannten Sprachen entwickelst, dieses Projekt OpenSource ist und auf GitHub liegt, dann solltest du unbedingt zwei Minuten aufwenden um den Build deines Projektes zu automatisieren. Selten bedarf es für einfache Projekte mehr Aufwand. Kommen nun beispielsweise noch Integrationstests dazu, welche das Deployment von SQL-Scripts, etc. voraussetzen, dann wird der Konfigurationsaufwand zwar selbstverständlich etwas höher, lohnt sich aber immer noch. Somit hat man als Entwickler, aber auch als Anwender immer die Gewissheit eines lauffähigen Projektes.

]]>
https://mspi.es/blog/Continuous-Integration-von-Node.js-Projekten-mit-GitHub-und-Travis-CI 2014-04-22-13-15 Tue, 22 Apr 2014 13:15:00 GMT
<![CDATA[OpenSource: Wie starte ich mein Projekt richtig?]]> Vor einiger Zeit habe ich mein erstes OpenSource-Projekt veröffentlicht. In diesem Artikel erkläre ich grob, welche Punkte man bei der Veröffentlichung beachten sollte. Es werden noch weitere Artikel folgen. In diesen gehe ich dann auf einige in diesem Artikel erwähnten Punkte genauer ein und zeige im Detail wie eine technische Umsetzung aussieht.

Um was geht es?

Für diesen Blog wollte ich keine Wordpress-Monster und Konsorten verwenden. Also habe ich mich nach leichtgewichtigen Blog-Engines umgeschaut und nicht nach einer eierlegenden Wollmilchsau, welche zusätzlich Theming, Templating und zig andere Features beinhaltet. Hierfür wollte ich einfach express.js nutzen. Eine Blog-Engine soll lediglich meine Artikel indizieren und mir eine einfach Möglichkeit bieten auf diese zuzugreifen. Alles was ich nun aber gefunden hatte war für meine Bedürfnisse entweder zu kompliziert, zu überfüllt oder nicht gut erweiterbar. Dann hatte ich den Entschluss gefasst mir selbst eine kleine Engine zu schreiben, genau wie ich sie mir vorstelle und sie dann als OpenSource-Projekt zu veröffentlichen. So kann ich nach jahrelanger Nutzung von OpenSource-Software der Community auch mal etwas zurückgeben.

bloggy

Die oben angesprochene Engine habe ich bloggy genannt. Es handelt sich um ein Node.js-Projekt. Die meisten Punkte in diesem Blog-Artikel sind aber von der Programmiersprache unabhängig und auch für kommerzielle Closed-Source-Projekte gültig. Mehr Worte will ich über bloggy hier nicht verlieren. Das kann jeder selbst auf GitHub nachlesen, wenn er möchte.

Erfahrungen

Ich habe mir vor der Veröffentlichung von bloggy viele Punkte aus bereits erlangten Erfahrungen oder bestehenden OpenSource-Projekten abgeschaut. Des Weiteren habe ich geprüft wie und wo man sein Projekt am besten bekannt macht. Diese Erfahrungen und Punkte habe ich in diesem Artikel zusammengefasst. Genaue Implementierungen wie Continuous Integration, Grunt, Testing, usw. werden in weiteren Artikeln folgen.

Recherche

Wichtig ist zuerst zu prüfen, ob bereits ein Projekt existiert, welches das abdeckt, was du entwickeln möchtest. Es spricht nichts dagegen nochmal etwas Ähnliches zu entwickeln. Dann solltest du dir aber in etwa folgende Fragen stellen:

  • Warum sollten die Leute lieber mein Projekt verwenden, als das bereits existierende?
  • Was sind die Vorteile?
  • Vielleicht besser erweiterbar?
  • Leichtgewichtiger?
  • Einfacher?

Was ist zu tun?

Die folgenden Punkte beschreiben meiner Meinung nach alle Aspekte, welche unbedingt vor dem ersten Bekanntmachen umgesetzt und beachtet werden sollten. Wenn du anderer Meinung bist oder noch etwas ergänzen möchtest, dann hinterlasse mir unten einfach einen Kommentar.

1. Projektname und Beschreibung

Einer der wichtigsten Punkte, welcher zudem häufig unterschätzt wird, ist der Name des Projekts und dessen Beschreibung. Der Name sollte am besten kurz, einfach zu merken und zu schreiben sein. Auch die Kurzbeschreibung ist sehr wichtig. Hier gilt auch das Prinzip: So wenig wie möglich, so viel wie nötig. Der Name, die Beschreibung, der Link zum Projekt und ein oder zwei Hashtags sollten locker in einen Tweet passen.

Bloggy Name und Beschreibung

Natürlich sollte der Name auch noch nicht vergeben sein. Also zumindest nicht als OpenSource-Projekt. Außerdem sollte natürlich ein Projektname gewählt werden, welcher nicht anstößig oder beleidigend für andere Entwickler wirken könnte. Aus diesem Grund wurde der Test-Runner Testacular in Karma umbenannt. Du solltest dir von daher besser einmal mehr, als einmal zu wenig Gedanken über den Namen machen. Wenn sich tatsächlich mal irgendwann eine "Szene" um dein Projekt gebildet haben sollte, dann hindert eine späte Umbenennung auch das Wachstum und ist mit einem großen Aufwand verbunden. Zum Beispiel müssen Wikis oder Dokumentationen angepasst werden, Links zu Projektseiten gehen eventuell nicht mehr, usw.

2. Lizenz

Als nächsten Punkt sollte eine Lizenz für das Projekt gewählt werden. Die MIT-Lizenz ist sehr verbreitet und kann für den privaten und geschäftlichen Einsatz verwendet werden. Das Ändern des Quellcodes ist natürlich auch erlaubt. Eine genauere aber dennoch übersichtliche Zusammenfassung der Unterschiede zwischen den vielen verschiedenen Lizenzen gibt es auf tl;drLegal.

3. Keep it simple

Je einfacher das Framework oder die Library verwendbar ist, desto besser. Man will nicht ellenlange Dokumentationen oder Wikis durchforsten müssen. Das heißt also, dass du sprechende Klassen- und Methodennamen, Fluent Interfaces, leichtgewichtige Strukturen, usw. verwenden solltest. Versuche dich reinzuversetzen, dass ein eventueller Benutzer erst einmal keine Ahnung von deiner API hat. Versuche also die Einstiegshürde so gering wie möglich zu halten.

Auch eine Integration des Projektes sollte so einfach vonstattengehen. Für viele Plattformen existieren Packagemanager, wie zum Beispiel nuget, npm, pip, RubyGems, usw. Auch hier sollte stets die aktuellste ausführbare Version der Projektes eingepflegt werden.

4. SemVer

Bei den Versionsnummern solltest du dich bei Node.js-Projekten immer an die SemVer-Spezifikation halten. SemVer steht für Semantic Versioning und beschreibt unter anderem, wie MAJOR-, MINOR- und PATCH-Versionen vergeben werden sollten. Auch die Art der Benennung von Alpha-Versionen oder wann Breaking API-Changes erlaubt sind ist spezifiziert.

5. Readme und Code-Beispiele

Dem Projekt darf eine Readme.md im Hauptverzeichnis nicht fehlen. Diese wird bei Github und BitBucket automatisch angezeigt, wenn man sich im Projekt-Root befindet. In dieser Readme-Datei kann man nun detaillierte Infos zum Projekt hinterlegen. Was sind die genauen Vorteile? Wie starte ich die Tests? Benötige ich noch Abhängigkeiten zum Kompilieren oder Ausführen? Wenn es sich um ein kleines Projekt handelt, dann können hier auch verwendbare Klassen/Methoden dokumentiert werden. Bei größeren Projekten ist ein Wiki eventuell besser dazu geeignet.

Da Entwickler grundsätzlich keine Dokumentation lesen wollen und Zeit auch immer Mangelware ist, darf eine Quickstart-Sektion nicht fehlen. In dieser sollten alle nötigen Schritte enthalten sein, damit man das Projekt schnell verwenden kann. Am besten auch mit einem kurzen Code-Snippet. Quasi eine tl;dr, dass man sofort loslegen kann. Zusätzlich sollte auch noch ein ausführbares Beispiel in der Versionsverwaltung enthalten sein.

6. Code-Style

Wenn mehrere Leute an einem Projekt arbeiten, dann sollten gewisse Regeln definiert sein, wie der Quellcode formatiert wird. In diesen Regeln ist unter anderem festgelegt wie Variablen benannt oder Abstände eingehalten werden. Ansonsten kann es schnell zu einem inkonsistenten Code-Bild kommen. Douglas Crockford hat die Code Conventions for the JavaScript Programming Language definiert, welche sehr weit verbreitet ist. Für C# gibt es die Framework Design Guidlines.

Damit der Code-Style auf immer konsequent eingehalten wird, gibt es für viele Sprachen Tools. Für C# existiert beispielsweise StyleCop, für JavaScript JsHint das ursprünglich aus JsLint entstanden ist.

7. Tests und Code-Coverage

Tests, Tests, Tests, Tests! Wir sind professionelle Entwickler, also muss unser Projekt getestet sein. Ich will schließlich keine Komponente verwenden, bei welcher ich hoffen muss, dass sie zuverlässig funktioniert. Welches Testframework genau zum Einsatz kommt ist prinzipiell erst einmal nicht so wichtig. Am besten ein bekanntes, das ohne Probleme installiert und ausgeführt werden kann. Empfehlen würde ich NUnit für .NET und Mocha für JavaScript/Node.js.

Zusätzlich sollte auch noch die Code-Coverage berechnet werden können. Somit hat man einen groben Überblick, wieviel Prozent des Codes mit Tests abgedeckt sind. Dies gibt bei einem Ergebnis von 100% zwar keine 100%-ige Sicherheit auf Fehlerfreiheit, aber rein psychologisch auf jeden Fall ein gutes Gefühl.

8. Automatisiertes Ausführen mit Grunt oder Scripts

Im Laufe eines Projektes werden bestimmte Aufgaben, wie beispielsweise das Ausführen von Unit-Tests, Kompilieren der Anwendung, Minifying oder Bundling (Asset Management) immer wieder ausgeführt. Dafür sind teilweise mehrere einzelne Schritte notwendig. Um potentiellen Mitentwicklern am Projekt den Einstieg und die Mitarbeit zu erleichtern solltest du diesen Aufwand so gering wie möglich halten. Makefiles, Batch- oder Shell-Scripte vereinfachen diesen Aufwand enorm. Anstatt zum Beispiel fünf verschiedene Befehle muss man sich nun nur noch einen merken und diesen ausführen. Gerade bei Schritten, welche häufig verwendet werden, macht sich diese Automatisierung in kürzester Zeit bezahlt.

Speziell im Node.js-Umfeld hat sich Grunt und die Verwendung von npm etabliert. In der Datei package.json können Scripte hinterlegt werden. Somit ist ein einheitlicher Weg geschaffen, um bestimmte Aktionen anzustoßen. Auf package.json - An interactive Guide gibt es neben der Beschreibung der scripts-Sektion auch Erläuterungen zu anderen Bereichen.

9. Continuous Integration

Es muss auf jeden Fall sichergestellt sein, dass sich das Projekt immer in einem kompilierbaren Zustand befindet. Es gibt sowohl für Node.js, als auch für .NET-Projekte öffentliche Dienste, wie Travis CI oder AppVeyor, welche automatisch nach einem Check-In den Build-Prozess anstoßen. Coveralls speichert dazu einen Verlauf der Test-Abdeckung. Wenn bereits Batch- oder Grunt-Scripts existieren, dann ist das Konfigurieren des CI-Prozesses im Handumdrehen erledigt.

10. Badges

Die meisten öffentlichen CI- oder Code-Coverage-Dienste stellen sogenannte Badges bereit. Badges sind dynamische Bilder, welche man problemlos in der Readme-Datei des Projektes referenzieren kann. In diesen wird dann zum Beispiel dargestellt, ob der Build-Vorgang erfolgreich war oder wieviel Code mit Tests abgedeckt ist. Sie werden direkt in die Readme eingebunden und schaffen Transparenz für etwaige Mitentwickler oder Anwender.

Bloggy Badges

11. Projekt-Hosting

Bei JavaScript-lastigen Projekten kann ich GitHub nur empfehlen. Ich würde sogar sagen, dass auch alle anderen Projekte, unabhängig von ihrer Sprache dort gut aufgehoben sind. Viele Entwickler besitzen einen GitHub-Account und haben Git sowieso auf ihrem System installiert und können so direkt loslegen. Desweiteren steht der soziale Charakter bei GitHub im Vordergrund. Man kann Projekte mit "Stars" versehen, Entwicklern folgen oder sie in Kommentaren oder Issues taggen. Aufgrund der hohen Verbreitung von Github, sind auch viele Build-Dienste darauf ausgelegt und optimiert. So kann normalerweise mit sehr geringem Aufwand eine Integration zwischen GitHub und anderen Diensten hergestellt werden.

Auf Pull-Requests von anderen Contributern wird auch der konfigurierte CI-Prozess automatisch angewandt. Somit kann man diese beruhigt akzeptieren, wenn sie erfolgreich, bzw. direkt überprüfen, warum diese nicht durchgelaufen sind.

GitHub Pull-Request CI

Hier zumindest noch eine kurze Liste mit einigen OpenSource-Hostern:

  1. GitHub
  2. BitBucket
  3. CodePlex
  4. SourceForge

12. Responsive sein

Wenn du dein Projekt nun frisch veröffentlicht hast und andere Entwickler sich dafür interessieren, dann solltest du schnell auf Fragen oder Probleme reagieren. Niemand will auf die Resonanz auf seinen Pull-Request eine Woche oder länger warten. Niemand will bei einem wichtigen Issue lange auf eine Problemlösung hoffen. Wenn das Projekt etwas "gereifter" ist, dann ist das IMO nicht mehr so kritisch, wie in der Anfangsphase.

13. Bekannt machen

Wenn du nun den Grundstein gelegt hat, du also alle Punkte von oben erfüllt, bzw. verinnerlicht hast, dann möchtst du natürlich auch eine möglichst große Masse an Entwicklern erreichen. Hierfür gibt es verschiedene Kanäle, die du verwenden kannst. Du kannst gezielt Leute per Mail benachrichtigen, einen Link auf deiner Timeline in Facebook/Twitter oder zum Beispiel auf Reddit posten. Und dann existieren noch spezielle Platformen oder News-Aggregatoren je nach Sprache.

Im Javascript-Umfeld wären das beispielsweise:

Wichtig ist, dass du nur das Nötigste in den Posts oder Tweets unterbringst. Maximal 160 Zeichen sollten ausreichen.

Fazit

Berücksichtigst du diese Liste an Punkten bei deinem OpenSource-Projekt, dann erlangt dieses einen sehr professionellen Touch. Selbstverständlich sind alle oben genannten Punkte auch für Closed-Source Projekte gültig, je nach Projektart aber vielleicht nicht so wichtig. In den nächsten paar Blog-Artikeln werde ich genauer auf die technische Implementierung eingehen. Stay tuned.

Bloggy war mein erstes OpenSource-Projekt. Was kann man noch besser machen?

Hinterlasst mir einen Kommentar oder twittert mir, falls ihr Ideen habt!

]]>
https://mspi.es/blog/OpenSource-Wie-starte-ich-mein-Projekt-richtig 2014-03-25-22-45 Tue, 25 Mar 2014 22:45:00 GMT
<![CDATA[Benutzerdefinierte Fehlerseiten von Node.js-Anwendungen]]> Eine typische Anforderung in Web-Projekten ist häufig die Darstellung von benutzerdefinierten Fehlerseiten für beispielsweise HTTP 404- oder HTTP 500-Fehler. Setzt man in Node.js auf das leichtgewichtige und weit verbreitete MVC-Framework express, so ist das auch mit wenigen Handgriffen umgesetzt. Nun wird man sich aber vermutlich wundern wo diese Fehlermeldungen nach dem Bereitstellen in Windows Azure geblieben sind. Die Lösung ist dabei relativ simpel.

Benutzerdefinierte Fehlerseiten in express

Beipiel 404-Fehler

Registrierung der Fehler-Handler

Das Registrieren der Fehler-Handler erfolgt hinter allen anderen Middlewares. Im Beispiel unten muss auf die Methoden-Signaturen geachtet werden.

Bei einem HTTP 404-Fehler wird der Status manuell auf 404 gesetzt und eine View mit dem Namen 404 dargestellt. Diese Methode wird aufgerufen, wenn der Request vorher noch von keiner Middleware bearbeitet wurde. Dies bedeutet auch, dass es sich hierbei eigentlich nicht um einen Fehler handelt und somit kein err-Parameter gebraucht wird.

Bei anderen Server-Fehlern, wie zum Beispiel HTTP 500, greift dann die untere Middleware. Dabei wird in der Anwendung mithilfe von throw ein Fehler-Objekt geworfen, welches sich im Parameter err wiederfindet. Ist ein Status gesetzt, so findet sich dieser als Statuscode in der Response wieder. Als Standard-Status ist 500 eingestellt und es wird die View 500 gerendert. Das Fehler-Objekt könnte hier um beliebige Felder erweitert werden.

app.use(function (req, res, next) {
    res.status(404).render('404');
});

app.use(function (err, req, res, next) {
    console.error(err.stack);
    res.status(err.status || 500).render('500');
});

Werfen von HTTP 400

Kann nun eine Route nicht gefunden werden, so wird schließlich der HTTP 404-Handler ausgeführt und somit die View 404.html oder 404.jade, je nach Layout-Engine, geladen.

Oft wird eine passende Route gefunden, es kann allerdings erst in dieser entschieden werden, ob eine Ressource existiert oder auch nicht. In diesem Fall muss in der Controller-Methode der next-Parameter existieren und mit return next() an die nächste Middleware in der Pipeline verwiesen werden. Dieser Vorgang simuliert das Verhalten einer nicht gefunden Route.

function (req, res, next) {
    if (!someNotFoundDatabaseEntity) {
        return next();
    }
}

Werfen von HTTP 500

Der HTTP 500-Handler wird aufgerufen, wenn ein Error mittels throw geworfen wurde. Am Fehler-Objekt sollte eine Eigenschaft status auf den gewünschten Status-Code gesetzt werden. Dieser Status-Code wird dem Webbrowser in der Response übermittelt.

Aktivieren der benutzerdefinierten Fehlerseiten in Windows Azure

Wenn die Anwendung nun in Windows Azure läuft so erscheint bei 404-Fehlern standardmäßig einfach nur die Meldung 'The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.' Auch 505-Fehler zeigen nur einen nicht formatierten Text an. Die implementierten benutzerdefinierten und an das Seiten-Layout angepassten Fehlermeldungen werden dabei ignoriert.

node.js läuft innerhalb des IIS und dieser hat in den Standardeinstellungen die Erlaubnis seine benutzerdefinierten Fehlermeldungen anzuzeigen. Die von node.js werden somit einfach überschrieben.

Dieses Verhalten kann natürlich umgestellt werden. Hierfür muss die Datei web.config angepasst werden.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <httpErrors existingResponse="PassThrough" /> <!-- Aktivierung von benutzerdefinierten Fehlerseiten in node.js -->

    <handlers>
      <add name="iisnode" path="server.js" verb="*" modules="iisnode"/>
    </handlers>

    <rewrite>
      <rules>
        <rule name="StaticContent">
          <action type="Rewrite" url="public{{REQUEST_URI}}"/>
        </rule>
        <rule name="DynamicContent">
          <conditions>
            <add input="{{REQUEST_FILENAME}}" matchType="IsFile" negate="True"/>
          </conditions>
          <action type="Rewrite" url="server.js"/>
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

Die Zeile <httpErrors existingResponse="PassThrough" /> im Beispiel oben ist die entscheidende. Der Tag befindet sich in der Konfiguration an der Stelle configuration/system.webServer/httpErrors. Der restliche XML-Inhalt ist nur beispielhaft und kann im eigenen Projekt abweichen.

]]>
https://mspi.es/blog/Benutzerdefinierte-Fehlerseiten-von-Node.js-Anwendungen 2014-03-18-13-00 Tue, 18 Mar 2014 13:00:00 GMT
<![CDATA[Remote-Debugging von Node.js-Anwendungen]]> In Software-Projekten will man natürlich stets eine hohe Testabdeckung erreichen, um so weit wie möglich, eventuelle Fehlerquellen zu eliminieren. Trotzdem kommt man normalerweise nicht um das traditionelle Debuggen herum. Gerade wenn es um die Integration diverser Bibliotheken oder Frameworks geht, kann ein Debugger ungemein dabei helfen Probleme zu erkennen und zu beseitigen. Als ein durch Visual Studio verwöhnter Entwickler ist die Komfort-Messlatte natürlich ziemlich hoch gesetzt. Es gibt glücklicherweise die Möglichkeit Node.js-Anwendungen mit einem grafischen Debugger zu steuern. Hierdurch bekommt man Zugriff auf Features, wie ein Watch-Window, Edit-Continue, Breakpoints, Call-Stack, usw. Node.js verwendet zur Kommunikation das V8 Debugger Protocol. Es bleiben prinzipiell keine Wünsche offen.

Um das Debuggen zu aktivieren muss zuerst der Node-Prozess mit dem Parameter --debug gestartet werden. Danach aktiviert man mit Hilfe des NPM-Moduls node-supervisor das Debuggen über einen beliebigen WebKit basierenden Browser, wie zum Beispiel Google Chrome oder Safari.

node --debug

Mit dem Befehl node --debug app.js wird die Anwendung inklusive Debugging-Support gestartet. Der Debugging-Service wird dabei standardmäßig auf Port 5858 hochgefahren. Ein Debugger kann sich nun auf den gewählten Port der lokalen Maschine verbinden und die Anwendung steuern.

node-inspector

Bei dem NPM-Paket node-inspector handelt es sich um ein Werkzeug, welches die Blink Developer Tools eines auf WebKit basierenden Browser mit node.js verbindet. Nach der globalen Installation mit dem Kommando npm install -g node-inspector ist das Tool von der Kommandozeile aus jederzeit sofort einsatzbereit. Die zu debuggende Node.js-Anwendung muss zuerst mit dem --debug Flag gestartet werden. Danach ruft man node-inspector auf und wechselt im Browser auf den von node-inspector bereitgestellten Link.

Run node-inspector

Nun sollten sich im Browser automatisch die Developer-Tools öffnen und die Anwendung kann komfortabel debuggt werden.

Use in Google Chrome

Das Remote-Debugging ist so von jedem beliebigen Rechner aus möglich. Es muss lediglich Port 8080 in der Firewall freigegeben sein. Dieser Port kann mit dem Parameter --web-port 1234 beim Starten von node-inspector aber auch noch angepasst werden. node-inspector verbindet hierbei den Debugging-Service (Port 5858) und die Developer Tools des Browsers über Websockets miteinander. Somit kommt hier eine standardisierte Technik und keine proprietären Tools zum Einsatz.

IDEs

Auf IDEs will ich in diesem Artikel nur der Vollständigkeit halber eingehen. Ich will mich mehr auf die plattform- und IDE-unabhängigen Lösungen konzentrieren.

Ja, in Webstorm existiert ein Debugger. Ja, in Visual Studio gibt es mit den Node.js Tools nun auch die Möglichkeit node.js-Anwendungen zu debuggen. Sogar in die Online-IDE Cloud9 IDE ist ein Debugger integriert.

Remote-Debugging in Windows Azure

Um das Remote-Debuggen von Windows Azure Web Sites zu ermöglichen müssen zuvor noch ein paar Einstellungen vorgenommen werden.

Der Datei iisnode.yml muss folgender Konfigurationsparameter hinzugefügt werden:

debuggingEnabled: true

Zusätzlich wird noch eine Rewrite-Rule in der web.config benötigt. Somit bringt man dem IIS bei, dass Requests an einen bestimmten URL-Pfad nicht von der Node.js-Anwendung, sondern vom node-inspector Prozess bearbeitet werden.

<system.webServer>
  [...]

  <rewrite>
    <rules>
      <rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
        <match url="^server.js\/debug[\/]?" />
      </rule>          
      <rule name="Application">
        <action type="Rewrite" url="server.js"/>
      </rule>
    </rules>
  </rewrite>
</system.webServer>

Wechselt man nun zur URL http://myapp.azurewebsites.net/server.js/debug, so öffnen sich die Developer Tools und die Anwendung kann debuggt werden. Der Debugger-Prozess kann durch Anhängen von ?kill an diese URL jederzeit beendet werden.

Aktiviert man das Debugging in Windows Azure, so entsteht hier natürlich eine Sicherheitslücke, da der Quellcode der Anwendung nun komplett einsehbar ist. Dies beinhaltet selbstverständlich auch alle eventuell verwendeten asswörter, Datenbankverbindungen, usw. Um hier Abhilfe zu schaffen, so sollte in der Datei iisnode.yml zusätzlich folgender Parameter gesetzt werden.

debuggerPathSegment: wqp232sdff382qq223

In der web.config muss die Rewrite-Rule dementsprechend angepasst werden:

<match url="^server.js\/debug[\/]?" />
  wird zu
<match url="^server.js\/wqp232sdff382qq223[\/]?" />

Der Token oben ist lediglich ein Beispiel und kann frei gewählt werden. Der Zugriffspfad auf den Debugger ändert sich nun in http://myapp.azurewebsites.net/server.js/wqp232sdff382qq223. Somit muss ein potentieller Angreifer diesen Link erraten, was relativ unwahrscheinlich ist. Nichtsdestotrotz sollte nach dem Debuggen der Zugriff sofort wieder deaktiviert werden, um ein eventuelles Sicherheitsrisiko zu vermeiden.

Fazit

Das Paket node-inspector in Kombination mit einem auf WebKit basierenden Browser bringt alle Features mit, welche für ein produktives Arbeiten notwendig sind. Man bekommt zusätzlich, aufgrund der Kommunikation über WebSockets, auch automatisch Remote-Unterstützung mitgeliefert. Das Aktivieren von Remote-Debugging in Windows Azure ist mit wenigen Handgriffen durchgeführt.

]]>
https://mspi.es/blog/Remote-Debugging-von-Node.js-Anwendungen 2014-03-10-22-00 Mon, 10 Mar 2014 22:00:00 GMT
<![CDATA[iisnode.yml: Konfigurieren von Node.js in Windows Azure]]> Wenn ich eine Node.js-Anwendung über Continuous Deployment in Windows Azure bereitstellen will, dann funktioniert das im einfachsten Fall, wie in meinem letzten Blog-Post beschrieben, ohne jegliche Konfiguration. Erfahrungsgemäß kann aber mit hoher Wahrscheinlichkeit jeder Software-Entwickler bestätigen, dass es bei diesem Fall niemals bleiben wird.

iisnode

Eine Node.js-Anwendung kann prinzipiell einfach über das Kommando node server.js gestartet werden. Dies ist allerdings für einen produktiven Einsatz nicht empfohlen. Unter Windows können Node.js-Anwendungen im IIS gehostet werden. Da dieser aber grundsätzlich erstmal nichts von Node.js kennt, kommt hierbei das Modul iisnode ins Spiel. Es verbindet node.exe, die bereitgestellte Anwendung und den IIS miteinander.

iisnode bringt mehrere Vorteile mit sich:

  • Prozess-Verwaltung: Die Web Site kann zentral gestartet, gestoppt und deren Status überprüft werden.
  • Skalierbarkeit bei Multicore-CPUs: Node.js läuft als Single-Thread-Prozess. iisnode kann mehrere Instanzen von Node.js starten und somit die Anwendung auf mehrere CPUs verteilen, sodass eine optimale Auslastung erreicht werden kann.
  • Auto-Refresh: Automatischer Neustart der Anwendung, wenn sich Dateien ändern oder ein Fehler die Anwendung zum Absturz bringt.
  • Debugging: Integration des NPM-Moduls node-inspector zum Remote-Debuggen der Anwendung von jedem WebKit-basierenden Browser aus.

Alle sonstigen Features des IIS und dessen Infrastruktur bleiben zusätzlich erhalten.

Wenn eine Node.js-Anwendung im Windows Azure Web Sites-Service läuft, dann wird im Hintergrund genau dieses iisnode-Modul verwendet.

iisnode.yml

Das iisnode-Modul lässt sich über die Datei iisnode.yml, welche im Hauptverzeichnis der Anwendung liegt, konfigurieren. Hier ein Beispiel mit einigen Einstellungen und deren Beschreibungen.

nodeProcessCommandLine: "D:\home\site\wwwroot\bin\node.exe"
node_env: production
loggingEnabled: false
devErrorsEnabled: false
debuggingEnabled: false

Eine komplett dokumentierte Version der Datei iisnode.yml findet sich im Repository von iisnode wieder.

Node-Version austauschen

Mit dem Parameter nodeProcessCommandLine ist es möglich von der von Windows Azure zur Verfügung gestellten node.exe-Version abzuweichen. Die entsprechende node.exe wird dabei einfach im Projekt-Ordner abgelegt (im Beispiel oben '\bin\node.exe').

Das Austauschen der node.exe kann nur empfohlen werden, da Windows Azure als Standard die veraltete Version 0.6.20 einsetzt und durch das Ändern der engine-Eigenschaft in der Datei package.json höchstens noch Version 0.8.4 zulässt. Nachzulesen ist das in der Windows Azure Dokumentation.

Anwendungsumgebung festlegen

node_env setzt die NODE_ENV-Umgebungsvariable, auf welche innerhalb der node.js-Anwendung zugegriffen werden kann. Die Variable kann z.B. auf production, development o.ä. gesetzt werden. Somit können in deren Abhängigkeit unterschiedliche Anwendungskonfigurationen geladen werden.

if (process.env.NODE_ENV == 'production') {
    console.log('Running in production.');
}

Logging

console.log()-Ausgaben eines in Azure laufenden Prozesses können direkt am Client überwacht werden. Mit azure site log download [sitename] kann der gesamte Log heruntergeladen werden. Ein Streaming der Logausgaben ist mit dem Kommando azure site log tail [sitename] möglich. Durch das Setzen von loggingEnabled: false kann dieses Verhalten deaktiviert werden. Mehr dazu in der Windows Azure Dokumentation.

Unerwartete Fehler

Tritt ein Fehler bei der Ausführung der Node.js-Anwendung auf, so werden die Fehlerdetails als HTTP 200-Response im Browser des Benutzers ausgegeben. Um keine kritischen Serverinformationen nach außen zu geben kann die Einstellung devErrorsEnabled: false dazu verwendet werden mit einer HTTP 500-Response ohne Meldung zu antworten.

Web.config

In dem Fall, wenn beim Deployment keine web.config im Root-Verzeichnis gefunden wurde, erzeugt und verwendet Azure automatisch diese Datei mit dem folgenden Inhalt:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <webSocket enabled="false" />
    <handlers>
      <add name="iisnode" path="server.js" verb="*" modules="iisnode"/>
    </handlers>
    <rewrite>
      <rules>
        <rule name="StaticContent">
          <action type="Rewrite" url="public{REQUEST_URI}"/>
        </rule>
        <rule name="DynamicContent">
          <conditions>
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
          </conditions>
          <action type="Rewrite" url="server.js"/>
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

Will man IIS-spezifische Einstellungen vornehmen, welche mit der Datei iisnode.yml nicht abgedeckt werden können, so sollte man dieses Template als Basis für die Anpassungen verwenden.

Fazit

Beim Deployment wird ein eindeutiges Opt-In-Prinzip angewandt. Das heißt unsere Anwendung läuft erst einmal mit den Grundeinstellungen und benötigt nur den eigentlichen Anwendungscode. Lediglich wenn ich nun präzisere Einstellungen vornehmen möchte oder muss, kann ich dies mit einem sehr geringen Konfigurationsaufwand in der Datei iisnode.yml oder web.config tun. Komplexe unverständliche Mechanismen oder Tools sind hier nicht notwendig.

]]>
https://mspi.es/blog/iisnode.yml-Konfigurieren-von-Node.js-in-Windows-Azure 2014-03-04-21-00 Tue, 04 Mar 2014 21:00:00 GMT
<![CDATA[Continuous Deployment von Azure Web Sites und Node.js]]> Als Entwickler will ich weitestgehend repetitive Tasks vermeiden, um mich mit meinen eigentlichen Aufgaben oder Problemen bei der Softwareentwicklung auseinandersetzen zu können. Von daher verwende ich natürlich Continuous Integration. In diesem Blog-Artikel geht es, genauer gesagt, um den Einsatz von Continuous Deployment eines Node.js-Projekts, welches beim Windows Azure Websites-Service gehostet ist. Genauso bin ich im Übrigen bei der Entwicklung dieses Blogs vorgegangen.

1. Projekt erstellen

Zuerst benötigst du ein Projekt. Am besten zuerst ganz klein anfangen um etwaige Fehlerquellen gering zu halten.

Erstelle einen leeren Ordner und lege darin eine Datei package.json mit dem folgenden Inhalt ab.

{
}

Installiere danach Connect. Der Parameter --save erzeugt dabei automatisch einen Eintrag in der Datei package.json zum automatisierten Wiederherstellen dieser Abhängigkeit.

$ npm install connect --save

Erzeuge danach eine Datei server.js.

var connect = require('connect');

connect.createServer(function (req, res) {
    res.writeHeader(200, {"Content-Type": "text/plain"});
    res.write('Hi, I am up and running!');
    res.end();
}).listen(process.env.PORT || 3000);

Mit der Eingabe von node server.js startest du den Dienst. Über http://localhost:3000 greifst du nun auf ihn zu.

Der Teil process.env.PORT || 3000 bewirkt, dass Port 3000 nur verwendet wird, falls die Umgebungsvariable PORT nicht auf eine andere Zahl gesetzt ist.

2. Projekt in der Quellcodeverwaltung ablegen und Azure Web Site erstellen

Wechsle nun zum Azure Management Portal und erstelle eine neue Azure Web Site. Wichtig ist, dass die Option "Publish from source control" gesetzt ist.

Windows Azure - Create Web Site

Durch das Setzen dieser Option hat man nun die Möglichkeit auszuwählen, wo der Quellcode der Web Site abgelegt ist.

Windows Azure - Where is your source code

Es kann zwischen verschiedenen Quellen gewählt werden:

  • Bei Visual Studio Online
  • Lokales Git Repository (Gehostet auf Windows Azure)
  • Bei BitBucket gehostetes Repository
  • Bei GitHub gehostetes Repository
  • Bei CodePlex gehostetes Repository
  • Beliebiges anderes, externes Repository (Git oder Mercurial)
  • Ein Ordner auf Dropbox

Wähle nun eine der Optionen aus und folge den Anweisungen des Assistenten. Ich würde mich für ein BitBucket- oder GitHub-Repository entscheiden, da diese Dienste eine gewisse Datensicherheit gewährleisten.

3. Deployment anstoßen

Nun, nichts leichter als das. Nehmen wir an, dass ein GitHub-Repository gewählt wurde. Alle Commits müssen lediglich auf den Server gepusht werden und das Deployment wird automatisch angestoßen.

Hierbei wird Windows Azure über den Push in das Repository benachrichtigt und geht dann automatisch wie folgt vor:

  1. Abrufen des aktuellen Standes des eingestellten Branch im Repository.
  2. Wiederherstellung aller NPM-Packages mit dem Kommando npm install.
  3. Setzen der in den Site-Einstellungen festgelegten Umgebungsvariablen, sowie der Variable PORT.
  4. Automatisches Starten des Node-Prozesses mithilfe von iisnode (Modul für den IIS) sobald Zugriff erfolgt. Hierbei wird eine JS-Datei mit dem Namen server.js erwartet.

Fazit

Das Einrichten eines CD-Prozesses ist in wenigen Minuten erledigt und funktioniert mit jedem Mercurial oder Git-Repository. Im einfachsten Fall müssen keine besonderen Einstellungen vorgenommen werden und die Node-Anwendung ist wenige Sekunden nach dem Checkin lauffähig. Man bekommt somit direkt Antwort, ob die Anwendung lauffähig ist und kann auf Probleme sofort reagieren. Auf den Entwicklersystemen muss hierfür nichts eingerichtet werden, da der komplette Vorgang serverseitig und automatisiert abläuft. Man kann auch zwei Azure Web Sites anlegen, wobei eine auf einen Development-Branch und eine auf den Production-Branch in der Quellcodeverwaltung hört. Somit ist das Testen der Site jederzeit unter realen Bedingungen in der Azure Cloud möglich, ohne dass das Produktiv-System angetastet wird. Wenn man noch detailliertere Einstellungen für node.js und den darüber laufenden IIS festlegen möchte, ist dies natürlich auch möglich. Hierfür kann man die Dateien iisnode.yml und web.config verwenden. Mehr dazu in den nächsten Blog-Posts.

]]>
https://mspi.es/blog/Continuous-Deployment-von-Azure-Web-Sites-und-Node.js 2014-03-02-22-20 Sun, 02 Mar 2014 22:20:00 GMT