Server-side
// Archive Manager Script Include
var ArchiveManager = Class.create();
ArchiveManager.prototype = {
initialize: function() {
this.BATCH_SIZE = 1000;
this.MAX_RUNTIME_MS = 300000; // 5 minutes
},
// Main archive execution method
executeArchiveRules: function(tableName, retentionDays) {
var startTime = new GlideDateTime();
var cutoffDate = new GlideDateTime();
cutoffDate.addDaysLocalTime(-retentionDays);
gs.info('Starting archive process for table: ' + tableName + ', cutoff date: ' + cutoffDate);
// Ensure archive table exists
if (!this._ensureArchiveTable(tableName)) {
gs.error('Failed to create archive table for: ' + tableName);
return false;
}
var totalArchived = 0;
var continueProcessing = true;
while (continueProcessing && this._withinTimeLimit(startTime)) {
var batch = this._getBatchToArchive(tableName, cutoffDate);
if (batch.length === 0) {
continueProcessing = false;
break;
}
var archivedCount = this._archiveBatch(tableName, batch);
totalArchived += archivedCount;
gs.info('Archived ' + archivedCount + ' records from ' + tableName + ', total: ' + totalArchived);
}
this._updateArchiveAudit(tableName, totalArchived, startTime);
return true;
},
// Create archive table with same structure as source
_ensureArchiveTable: function(tableName) {
var archiveTableName = tableName + '_archive';
var gr = new GlideRecord('sys_db_object');
gr.addQuery('name', archiveTableName);
gr.query();
if (!gr.next()) {
// Create archive table by cloning structure
var sourceTable = new GlideRecord('sys_db_object');
sourceTable.addQuery('name', tableName);
sourceTable.query();
if (sourceTable.next()) {
var newTable = new GlideRecord('sys_db_object');
newTable.initialize();
newTable.setValue('name', archiveTableName);
newTable.setValue('label', sourceTable.getValue('label') + ' Archive');
newTable.setValue('super_class', sourceTable.getValue('super_class'));
newTable.setValue('sys_class_name', 'sys_db_object');
var tableId = newTable.insert();
if (tableId) {
this._cloneTableStructure(tableName, archiveTableName);
return true;
}
}
return false;
}
return true;
},
// Get batch of records to archive
_getBatchToArchive: function(tableName, cutoffDate) {
var batch = [];
var gr = new GlideRecord(tableName);
gr.addQuery('sys_updated_on', '<', cutoffDate);
gr.addQuery('state', 'IN', '3,7,8'); // Closed states for tasks
gr.orderBy('sys_updated_on');
gr.setLimit(this.BATCH_SIZE);
gr.query();
while (gr.next()) {
batch.push({
sys_id: gr.getUniqueValue(),
record: this._serializeRecord(gr)
});
}
return batch;
},
// Archive a batch of records
_archiveBatch: function(tableName, batch) {
var archiveTableName = tableName + '_archive';
var archivedCount = 0;
try {
// Insert into archive table
for (var i = 0; i < batch.length; i++) {
var archiveRecord = new GlideRecord(archiveTableName);
this._deserializeRecord(archiveRecord, batch[i].record);
archiveRecord.setValue('archived_date', new GlideDateTime());
archiveRecord.setValue('archive_reason', 'Automated retention policy');
if (archiveRecord.insert()) {
archivedCount++;
}
}
// Delete from source table after successful archive
if (archivedCount === batch.length) {
for (var j = 0; j < batch.length; j++) {
var sourceRecord = new GlideRecord(tableName);
if (sourceRecord.get(batch[j].sys_id)) {
sourceRecord.deleteRecord();
}
}
}
} catch (e) {
gs.error('Error archiving batch: ' + e.message);
}
return archivedCount;
},
// Serialize record data
_serializeRecord: function(gr) {
var data = {};
var fields = gr.getFields();
for (var i = 0; i < fields.size(); i++) {
var field = fields.get(i);
data[field.getName()] = gr.getValue(field.getName());
}
return data;
},
// Deserialize record data
_deserializeRecord: function(gr, data) {
for (var field in data) {
if (data.hasOwnProperty(field) && field !== 'sys_id') {
gr.setValue(field, data[field]);
}
}
},
// Check if within time limit
_withinTimeLimit: function(startTime) {
var currentTime = new GlideDateTime();
var elapsed = currentTime.getNumericValue() - startTime.getNumericValue();
return elapsed < this.MAX_RUNTIME_MS;
},
// Update archive audit trail
_updateArchiveAudit: function(tableName, recordCount, startTime) {
var audit = new GlideRecord('u_archive_audit');
audit.initialize();
audit.setValue('table_name', tableName);
audit.setValue('records_archived', recordCount);
audit.setValue('archive_date', startTime);
audit.setValue('duration_ms', new GlideDateTime().getNumericValue() - startTime.getNumericValue());
audit.insert();
},
type: 'ArchiveManager'
};
This implementation creates a comprehensive archiving system that safely moves aged records to dedicated archive tables while maintaining data integrity. The code uses batched processing to avoid performance issues, includes time limits to prevent long-running operations, and maintains an audit trail of all archiving activities. Key features include automatic archive table creation, serialization of complex record data, and transaction safety through insert-then-delete operations.