Skip to main content

Plugins: Tasks

Another type of plugins gives us the ability to perform various actions during server/database startup process or enables us to perform actions periodically. For these instances we have introduced two interfaces and one abstract class.

public interface IStartupTask
{
void Execute(DocumentDatabase database);
}

public interface IServerStartupTask
{
void Execute(RavenDbServer server);
}
public abstract class AbstractBackgroundTask : IStartupTask
{
private static readonly ILog log = LogManager.GetCurrentClassLogger();

public DocumentDatabase Database { get; set; }

public void Execute(DocumentDatabase database)
{
Database = database;
Initialize();
Task.Factory.StartNew(BackgroundTask, TaskCreationOptions.LongRunning);
}

protected virtual void Initialize()
{
}

int workCounter;
public void BackgroundTask()
{
string name = GetType().Name;
WorkContext context = Database.WorkContext;
while (context.DoWork)
{
bool foundWork = false;
try
{
foundWork = HandleWork();
}
catch (Exception e)
{
log.ErrorException("Failed to execute background task", e);
}
if (foundWork == false)
{
context.WaitForWork(TimeoutForNextWork(), ref workCounter, name);
}
else
{
context.UpdateFoundWork();
}
}
}

protected virtual TimeSpan TimeoutForNextWork()
{
return TimeSpan.FromHours(1);
}

protected abstract bool HandleWork();
}

where:

  • IStartupTask can be used to implement a task which will be started during database initialization.
  • IServerStartupTask can be used to implement a task which will be started during server initialization.
  • AbstractBackgroundTask is a base for all periodic tasks.

Example - Send email when server is starting

public class SendEmailWhenServerIsStartingTask : IServerStartupTask
{
public void Execute(RavenDbServer server)
{
MailMessage message = new MailMessage("ravendb@myhost.com", "admin@myhost.com")
{
Subject = "RavenDB server started.",
Body = "Start at: " + DateTime.Now.ToShortDateString()
};

using (SmtpClient smtpClient = new SmtpClient("mail.myhost.com"))
{
smtpClient.Send(message);
}
}
}

Example - Perform a cleanup task during database initialization

public class CleanupWhenDatabaseIsStarting : IStartupTask
{
private const string SpecificDatabaseName = "Northwind";

public void Execute(DocumentDatabase database)
{
if (database.Name != SpecificDatabaseName)
return;

using (CancellationTokenSource cts = new CancellationTokenSource())
{
bool stale;
IEnumerable<string> queryResults = database.Queries.QueryDocumentIds(
"Notifications/Temp",
new IndexQuery(),
CancellationTokenSource.CreateLinkedTokenSource(database.WorkContext.CancellationToken, cts.Token),
out stale);

foreach (string documentId in queryResults)
{
database.Documents.Delete(documentId, null, null);
}
}
}
}

Example - Perform a cleanup task every six hours

public class RemoveAllTemporaryNotificationsTask : AbstractBackgroundTask
{
protected override bool HandleWork()
{
QueryResultWithIncludes queryResults = Database.Queries.Query("Notifications/Temp", new IndexQuery(), Database.WorkContext.CancellationToken);
foreach (RavenJObject document in queryResults.Results)
{
string id = ((RavenJObject)document["@metadata"]).Value<string>("@id");
Database.Documents.Delete(id, null, null);
}

return true;
}

protected override TimeSpan TimeoutForNextWork()
{
return TimeSpan.FromHours(6);
}
}