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);
}
}