This takes one table in master, two stored procedures, and one job that is set to execute on startup and calls the first stored procedure ([master].[dbo].[auto_MemoryReconfigure]). I set the default memory to use all but 3 gb on each node. That allows enough for the OS, and for a failover to start. Once the first failover is detected, the memory on all instances gets rebalanced to use in total only what each server has. You will also need linked servers set up on each instance in the cluster to all other instances on the cluster. This written for a 2-node cluster, but could be used as a framework for more than that. If you've got a memory-constrained cluster like I do, it REALLY helps out.
Table Definition:
CREATE TABLE [dbo].[ClusterMemory](
[Instance] [varchar](50) NOT NULL,
[DefaultNode] [varchar](50) NOT NULL,
[DefaultMemory] [int] NOT NULL,
[FailoverMemory] [int] NOT NULL,
[Failed] [bit] NOT NULL,
CONSTRAINT [PK_ClusterMemory] PRIMARY KEY CLUSTERED
(
[Instance] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[ClusterMemory] ADD CONSTRAINT [DF_ClusterMemory_Failed] DEFAULT ((0)) FOR [Failed]
This procedure should be automatically executed at startup, either automatically (I couldn't get it to work right) or by a job that executes at Agent startup.
ALTER proc [dbo].[auto_MemoryReconfigure]
as
SET ANSI_NULLS ON
SET ANSI_WARNINGS ON
/****************************************************************************/
/* Procedure to reconfigure memory on a clustered instance. */
/* Procedure will automatically execute at startup by a job. */
/* Expects: No Parameters */
/* Requires table master.dbo.ClusterMemory */
/* Calls stored procedure master.dbo.MemoryReconfigure */
/* */
/* Written by: Murray DeVore */
/* Date Written:07/29/2009 */
/* Revision Log */
/* Revised By Date Comments */
/* ------------- ---------- --------------------------------------------*/
/****************************************************************************/
declare @InstanceName as nvarchar(128)
declare @ServerName as nvarchar(128)
declare @DefaultNode as nvarchar(128)
declare @MemoryToUse as int
declare @MemoryInUse as int
set @InstanceName = (select @@SERVERNAME)
set @ServerName = (select cast(SERVERPROPERTY('ComputerNamePhysicalNetBIOS') as nvarchar))
set @DefaultNode = (select DefaultNode from [].master.dbo.ClusterMemory WHERE Instance = @InstanceName)
set @MemoryInUse = (select cast(value_in_use as int) from sys.configurations where name = 'max server memory (MB)')
/*Check to see if instance is running where it is supposed to and set memory accordingly*/
IF @DefaultNode = @ServerName
BEGIN
set @MemoryToUse = (select DefaultMemory from [].master.dbo.ClusterMemory WHERE Instance = @InstanceName)
UPDATE [].master.dbo.ClusterMemory SET Failed = 'False' WHERE Instance = @InstanceName
print 'Default Node'
END
ELSE
BEGIN
set @MemoryToUse = (select FailoverMemory from [].master.dbo.ClusterMemory WHERE Instance = @InstanceName)
UPDATE [].master.dbo.ClusterMemory SET Failed = 'True' WHERE Instance = @InstanceName
print 'Fail Node'
END
IF @MemoryInUse @MemoryToUse
BEGIN
exec sp_configure 'max server memory (MB)',@MemoryToUse
RECONFIGURE
print @InstanceName+' RECONFIGURED. Memory --> '+cast(@MemoryTouse as varchar)
END
ELSE print @InstanceName+' NOT RECONFIGURED. Memory --> '+cast(@MemoryTouse as varchar)
/*Send email/page alert */
declare @sub as varchar(100)
declare @msg as varchar(1000)
declare @to as varchar(100)
set @msg = 'Instance '+@InstanceName+' has restarted and is now running on node '+@ServerName+'. Its default node is '+@DefaultNode+'.'
SET @to = (select pager_address from msdb..sysoperators where name = 'SQL Server DBA')
--set @to = (select email_address from msdb..sysoperators where name = 'SQL Server DBA')
set @sub = 'WARNING: Instance '+@InstanceName+' has restarted.'
exec msdb.dbo.sp_send_dbmail @recipients = @to
, @subject = @sub
, @body = @msg
/*Tell the other instances on the cluster to check themselves*/
DECLARE @sql varchar(1000)
DECLARE curInstance CURSOR
READ_ONLY
FOR select 'EXEC '+QUOTENAME(Instance)+'.master.dbo.MemoryReconfigure' from [].master.dbo.ClusterMemory where Instance @InstanceName
OPEN curInstance
FETCH NEXT FROM curInstance INTO @sql
WHILE (@@fetch_status -1)
BEGIN
IF (@@fetch_status -2)
BEGIN
exec (@sql)
END
FETCH NEXT FROM curInstance INTO @sql
END
CLOSE curInstance
DEALLOCATE curInstance
This stored proc is called by the first one, only on each of the other instances on the cluster.
ALTER proc [dbo].[MemoryReconfigure]
as
SET ANSI_NULLS ON
SET ANSI_WARNINGS ON
/****************************************************************************/
/* Procedure to reconfigure memory on a clustered instance. */
/* Procedure is called remotely by auto_MemoryReconfigure */
/* Expects: No Parameters */
/* Requires table master.dbo.ClusterMemory */
/* */
/* Written by: Murray DeVore */
/* Date Written:07/29/2009 */
/* Revision Log */
/* Revised By Date Comments */
/* ------------- ---------- --------------------------------------------*/
/****************************************************************************/
declare @InstanceName as nvarchar(128)
declare @ServerName as nvarchar(128)
declare @DefaultNode as nvarchar(128)
declare @MemoryToUse as int
declare @MemoryInUse as int
declare @FailedCount as int
set @InstanceName = (select @@SERVERNAME)
set @ServerName = (select cast(SERVERPROPERTY('ComputerNamePhysicalNetBIOS') as nvarchar))
set @MemoryInUse = (select cast(value_in_use as int) from sys.configurations where name = 'max server memory (MB)')
set @FailedCount = (select count(Failed) from [].master.dbo.ClusterMemory WHERE Failed = 'True')
IF @FailedCount = 0
/* Make sure memory is set to default allocation */
BEGIN
set @MemoryToUse = (select DefaultMemory from [].master.dbo.ClusterMemory WHERE Instance = @InstanceName)
END
ELSE
/* Failover condition, reduced memory allocation */
BEGIN
set @MemoryToUse = (select FailoverMemory from [].master.dbo.ClusterMemory WHERE Instance = @InstanceName)
END
IF @MemoryInUse @MemoryToUse
BEGIN
exec sp_configure 'max server memory (MB)',@MemoryToUse
RECONFIGURE
print @InstanceName+ ' RECONFIGURED. Memory --> '+cast(@MemoryTouse as varchar)
END
ELSE print @InstanceName+ ' NOT RECONFIGURED. Memory --> '+cast(@MemoryTouse as varchar)