#!/usr/bin/python """ pg_generate_conf Sample usage: pg_generate_conf Reads that config file, updates a few key configuration settings, then writes result to standard output (so far) """ import sys import os import datetime class PGConfigLine: """ Stores the value of a single line in the postgresql.conf file, with the following fields: lineNumber : integer originalLine : string commentSection : string setsParameter : boolean If setsParameter is True these will also be set: name : string readable : string raw : string This is the actual value delimiter (expectations are ' and ") """ def __init__(self,line,num=0): self.originalLine=line self.lineNumber=num self.setsParameter=False # Remove comments and edge whitespace self.commentSection="" commentIndex=line.find('#') if commentIndex >= 0: line=line[0:commentIndex] self.commentSection=line[commentIndex:] line=line.strip() if line == "": return # Split into name,value pair equalIndex=line.find('=') if equalIndex<0: return (name,value)=line.split('=') name=name.strip() value=value.strip() self.name=name; self.setsParameter=True; # Many types of values have ' ' characters around them, strip # TODO Set delimiter based on whether there is one here or not value=value.rstrip("'") value=value.lstrip("'") self.readable=value def outputFormat(self): s=self.originalLine; return s # Implement a Java-ish interface for this class def getName(self): return self.name def getValue(self): return self.readable def getLineNumber(self): return self.lineNumber def isSetting(self): return self.setsParameter def toString(self): s=str(self.lineNumber)+" sets?="+str(self.setsParameter) if self.setsParameter: s=s+" "+self.getName()+"="+self.getValue() # TODO: Include commentSection, readable,raw, delimiter s=s+" originalLine: "+self.originalLine return s class PGConfigFile: """ Read, write, and manage a postgresql.conf file There are two main structures here: configFile[]: Array of PGConfigLine entries for each line in the file settingLookup: Dictionary mapping parameter names to the line that set them """ def __init__(self, filename): self.readConfigFile(filename) def readConfigFile(self,filename): self.filename=filename self.settingsLookup={} self.configFile=[] lineNum=0; for line in open(filename): line=line.rstrip('\n') lineNum=lineNum + 1 configLine=PGConfigLine(line,lineNum) self.configFile.append(configLine) if configLine.isSetting(): self.settingsLookup[configLine.getName()]=configLine def updateSetting(self,name,newValue): newLineText=str(name)+" "+str(newValue)+" # pg_generate_conf wizard "+str(datetime.date.today()) newLine=PGConfigLine(newLineText) if self.settingsLookup.has_key(name): # Comment out old line oldLine=self.settingsLookup[name] oldLineNum=oldLine.getLineNumber() commentedLineText="# "+oldLine.outputFormat() commentedLine=PGConfigLine(commentedLineText,oldLineNum) # Subtract one here to adjust for zero offset of array. # Any future change that adds lines in-place will need to do something # smarter here, because the line numbers won't match the array indexes # anymore self.configFile[oldLineNum-1]=commentedLine self.configFile.append(newLine) self.settingsLookup[name]=newLine def writeConfigFile(self,fileHandle): for l in self.configFile: print l.outputFormat() def debugPrintInput(self): print "Original file:" for l in self.configFile: print l.toString() def debugPrintSettings(self): print "Settings listing:" for k in self.settingsLookup.keys(): print k,'=',self.settingsLookup[k].getValue() def totalMemory(): # Should work on UNIX and Mac OS platforms physPages = os.sysconf("SC_PHYS_PAGES") pageSize = os.sysconf("SC_PAGE_SIZE") totalMemory = physPages * pageSize return totalMemory def wizardTune(config): mb=1024*1024 osOverhead=256 * mb; memAvail=totalMemory() - osOverhead; if memAvail>=(osOverhead / 2): # The net effect of the above is that if you don't have at least 384MB # of physical memory, we don't do any tuning suggestions right now. # The simple formulas below presume a relatively modern system with at # least that much RAM available sharedMB=(memAvail / 4) / mb; cacheSizeMB=(memAvail * 3 / 4) / mb; config.updateSetting('shared_buffers',"%d" % sharedMB+"MB") config.updateSetting('effective_cache_size',"%d" % cacheSizeMB+"MB") if __name__=='__main__': debug=False configFile=sys.argv[1] config=PGConfigFile(configFile) if (debug): config.debugPrintInput() print config.debugPrintSettings() wizardTune(config) config.writeConfigFile(sys.stdout)