Oren Eini

CEO of RavenDB

a NoSQL Open Source Document Database

Get in touch with me:

oren@ravendb.net +972 52-548-6969

Posts: 7,640
|
Comments: 51,260
Privacy Policy · Terms
filter by tags archive
time to read 2 min | 201 words

Thoughts?

  • Starting from a stable origin
  • Planning our DSL's versioning story
    • Implications of modifying the DSL Engine
    • Implications of modifying the DSL API and Model
    • Implications of modifying the DSL Syntax
    • Implications of modifying the DSL Environment
  • Regression Test Suite
  • Versioning cut off point - where to apply versioning concerns
  • Versioning strategies:
    • Abandon Ship Strategy
    • Glacial Change Strategy
    • Additive Change Strategy
    • Tower of Babel Strategy
    • Runtime Adapter Strategy
    • Compilation Adapter Strategy
    • Auto Modification Strategy
    • Self Modification Strategy
  • Versioning best practices:
    # actual content
    • Closed world
    • Regression test suite
time to read 1 min | 105 words

image This post from Jeffrey Palermo has managed to distilled the reasons for a lot of things that I consider important.

From persistence ignorance to dependency injection.

To be rather more exact, Jeffrey has managed to put it in words in a way that I don't think that I would have ever could.

Go and read it.

The concepts will probably not be earth shattering, but the ability to communicate clearly about this is very important.

 

 

 

 

.

time to read 1 min | 105 words

The title says it all, I think. Okay, it doesn't, I admit.

There is a lot of focus with DSL about the syntax. And there is some focus on the engine. There is very little focus about how both the environment and the usage of the DSL affect the DSL itself. Here are a few examples:

  • Naming convention
  • Script ordering
  • Execution location (when you are running the scripts)

All of those are of particular importance in many DSL, not only for the actual execution, but for how the DSL itself is written.

time to read 2 min | 264 words

Or, pay attention to how you write to the disk. Here is a simple example:

static void Main(string[] args)
{
	var count = 10000000;

	Stopwatch stopwatch = Stopwatch.StartNew();

	using (var stream = CreateWriter())
	using (var bw = new BinaryWriter(stream))
	{
		for (var i = 0; i < count; i++)
		{
			bw.Write(i);
		}
		bw.Flush();
	}
	stopwatch.Stop();
	Console.WriteLine("Binary Writer: " + stopwatch.ElapsedMilliseconds);

	stopwatch = Stopwatch.StartNew();

	using (var stream = CreateWriter())
	{
		for (var i = 0; i < count; i++)
		{
			var bytes = BitConverter.GetBytes(i);
			stream.Write(bytes, 0, 4);
		}
		stream.Flush();
	}
	stopwatch.Stop();
	Console.WriteLine("BitConverter: " + stopwatch.ElapsedMilliseconds);


	stopwatch = Stopwatch.StartNew();

	using (var stream = CreateWriter())
	using (var ms = new MemoryStream())
	{
		for (var i = 0; i < count; i++)
		{
			var bytes = BitConverter.GetBytes(i);
			ms.Write(bytes, 0, 4);

		}
		var array = ms.ToArray();
		stream.Write(array, 0, array.Length);
		stream.Flush();
	}
	stopwatch.Stop();
	Console.WriteLine("Memory stream: " + stopwatch.ElapsedMilliseconds);


	stopwatch = Stopwatch.StartNew(); 
using (var stream = CreateWriter()) { byte[] buffer = new byte[sizeof(int) * count]; int index = 0; for (var i = 0; i < count; i++) { buffer[index++] = (byte)i; buffer[index++] = (byte)(i >> 8); buffer[index++] = (byte)(i >> 16); buffer[index++] = (byte)(i >> 24); } stream.Write(buffer, 0, buffer.Length); stream.Flush(); } stopwatch.Stop(); Console.WriteLine("Single buffer: " + stopwatch.ElapsedMilliseconds); } private static FileStream CreateWriter() { return new FileStream(Path.GetTempFileName(), FileMode.Create, FileAccess.Write, FileShare.Read, 0x10000, FileOptions.SequentialScan | FileOptions.WriteThrough); }

And the results:

Binary Writer: 1877
BitConverter: 1985
Memory stream: 1702
Single buffer: 1022

time to read 1 min | 126 words

I found something extremely surprising while profiling a project. Take a look at this piece of code:

Stopwatch stop = Stopwatch.StartNew();
for (int i = 0; i < 1000000; i++)
{
	new WeakReference(null);
}
stop.Stop();
Console.WriteLine("WeakRef: " + stop.ElapsedMilliseconds);

stop = Stopwatch.StartNew();
for (int i = 0; i < 1000000; i++)
{
	new string('a', 5);
}
stop.Stop();
Console.WriteLine("'aaaaa': " + stop.ElapsedMilliseconds);

On my machine, this has the following output:

WeakRef: 980
'aaaaa': 35

Creating a WeakReference is much more costly than creating a normal object. Not surprising, when you think of it, WeakReference has deep ties to the CLR, but I couldn't really believe it when I saw it the first time.

time to read 3 min | 451 words

The use of finalizer to ensure eventual release of resources is recommended, but one thing that is not discussed often is the implications of adding a finalizer.

No, I am not talking about the extra wait time before the class memory is released, this is fairly well known. I am talking about two major additional issues that don't get enough attention, in my opinion.

Specifically, invalid instances and threading.

An object that has a finalizer will may be executed in more than one thread. Luckily, the CLR ensure that our finalizer will not be able to run if there is any code that is using the class, so it is not as bad as it seems at first.

However, something that make absolute sense was a bit of a surprise to me, when I figured it out, finalizers will run for invalid instances as well. What do I mean by that? Invalid instance is an object whose ctor threw an exception. It is in invalid state.

But, before it threw an exception, it might have acquired resources that needs to be released, so it goes to the GC finalizer as well. In fact, since you can't call Dispose on such an object, it must go to the finalizers for cleanup.

Remember the invalid state bit part? This is important!

Your object is in invalid state, and it is asked to cleanup itself. If it can't do so even in invalid state, an exception will be thrown. This exception on the finalizer thread will kill the application.

Here is the proof of concept code:

public class BadClass : IDisposable
{
	FileStream fs;

	public BadClass(int i)
	{
		if (i%2 == 0)
			throw new InvalidDataException("evens are bad for your health");
		fs = new FileStream(Path.GetTempFileName(), FileMode.Create);
	}

	~BadClass()
	{
		fs.Dispose();
	}

	public void Dispose()
	{
		fs.Dispose();
		GC.SuppressFinalize(this);
	}
}

And the code to execute it:

for (int i = 0; i < 1000; i++)
{
	try
	{
		using (new BadClass(i))
		{

		}
	}
	catch (Exception)
	{
	}
}

Trying to figure out what is going on in this scenario is tough. Going back to the threaded aspect of it. What this means is that such an object is a time bomb, and it will blow up at some future date to kill your application, but you are not likely to know what exactly caused it to be this way.

In my situation, I figured it out by putting new StackTrace(true) in the constructor of the object, and breaking into the debugger on the finalizer.  But until it occurred to me to look for it there...

time to read 2 min | 333 words

Update: To clarify, the only purpose of this code is to give atomic writes, it either complete successfully or it is rolled back.

Okay, this looks like it is too simple to work, thoughts?

/// <summary>
/// Allow to overwrite a file in a transactional manner.
/// That is, either we completely succeed or completely fail in writing to the file.
/// Read will correct previous failed transaction if a previous write has failed.
/// Assumptions:
///  * You want to always rewrite the file, rathar than edit it.
///  * The underlying file system has at least transactional metadata.
///  * Thread safety is provided by the calling code.
/// 
/// Write implementation:
///  - Rename file to "[file].old_copy" (overwrite if needed
///  - Create new file stream with the file name and pass it to the client
///  - After client is done, flush & close the stream
///  - Delete old file
/// 
/// Read implementation:
///  - If old file exists, remove new file and rename old file
/// 
/// </summary>
public static class TransactionalFile
{
	public static void Read(string name, Action<Stream> action)
	{
		if (File.Exists(name + ".old_copy"))
		{
			File.Delete(name);
			File.Move(name + ".old_copy", name);
		}

		using (
			var stream = new FileStream(name, FileMode.Open, FileAccess.Read, FileShare.None, 0x1000,
										FileOptions.SequentialScan))
		{
			action(stream);
		}
	}

	public static void Write(string name, Action<Stream> action)
	{
		File.Delete(name + ".old_copy");

		if (File.Exists(name))
			File.Move(name, name + ".old_copy");

		using (
			var stream = new FileStream(name, FileMode.CreateNew, FileAccess.Write, FileShare.None, 0x1000,
			                            FileOptions.WriteThrough | FileOptions.SequentialScan))
		{
			action(stream);
			stream.Flush();
		}

		File.Delete(name + ".old_copy");
	}
}

FUTURE POSTS

No future posts left, oh my!

RECENT SERIES

  1. API Design (10):
    29 Jan 2026 - Don't try to guess
  2. Recording (20):
    05 Dec 2025 - Build AI that understands your business
  3. Webinar (8):
    16 Sep 2025 - Building AI Agents in RavenDB
  4. RavenDB 7.1 (7):
    11 Jul 2025 - The Gen AI release
  5. Production postmorterm (2):
    11 Jun 2025 - The rookie server's untimely promotion
View all series

Syndication

Main feed ... ...
Comments feed   ... ...