Why I don't like FireBirdPart II

time to read 2 min | 272 words

Take a look at this code. It is supposed to give me the earliest message, ensuring that I'll get each message once and only once.

It doesn't work. I am getting some messages twice. The same code (well, simplified) just works on SQLite.

public QueueMessage GetEarliestMessage()
{
	byte[] data = null;
	bool done = false;
	while (done == false)
	{
		Transaction(cmd =>
		{
			/* 
				SELECT FIRST 1 Id, Data FROM IncomingMessages
				ORDER BY InsertedAt ASC
			*/
			cmd.CommandText = Queries.GetEarliestMessageFromIncomingQueue;
			string id;
			using (var reader = cmd.ExecuteReader())
			{
				if (reader.Read() == false)
				{
					done = true;
					return;
				}
				id = reader.GetString(0);
				data = (byte[])reader[1];
			}
			/* DELETE FROM IncomingMessages WHERE Id = @Id	*/
			cmd.CommandText = Queries.DeleteMessageFromIncomingQueue;
			cmd.Parameters.Add("@Id", id);
			try
			{
				var rowAffected = cmd.ExecuteNonQuery();
				// someone else already grabbed and deleted this row, 
				// so we will try again with another one
				if (rowAffected != 1)
					return; // same as continue in this case} 
			}
			catch (FbException e)
			{
				// yuck! it would have been better to compare the error code
				// but FB doesn't exposes it
				if (e.Message == "cannot update erased record")
				{
					return;// same as continue
				}
			}
			done = true;// same as break from the loop
		});
	}
	if (data == null)
		return null;
	return Deserialize(data);
}

protected void Transaction(Action<FbCommand> action)
{
	using (var connection = new FbConnection(connectionString))
	using (var cmd = connection.CreateCommand())
	{
		connection.Open();
		using (var tx = connection.BeginTransaction(IsolationLevel.Serializable))
		{
			cmd.Transaction = tx;
			action(cmd);
			tx.Commit();
		}
	}
}

a

More posts in "Why I don't like FireBird" series:

  1. (07 Jul 2008) Part II
  2. (07 Jul 2008) Part I