diff --git a/httpd.py b/httpd.py index 2e92c27..8432312 100644 --- a/httpd.py +++ b/httpd.py @@ -58,32 +58,14 @@ _LIB_CACHE = {} # Cache for dashboard static files (HTML, CSS, JS) _STATIC_CACHE = {} -# Optional memory profiling (installed via requirements.txt) -try: - import objgraph - _has_objgraph = True -except ImportError: - _has_objgraph = False - -try: - from pympler import muppy, summary - _has_pympler = True -except ImportError: - _has_pympler = False - -# Memory tracking for leak detection -_memory_samples = [] -_memory_sample_max = 60 # Keep last 60 samples (5 min at 5s intervals) -_peak_rss = 0 -_start_rss = 0 - # Cache for expensive operations -_gc_objects_cache = {'value': 0, 'time': 0} -_gc_objects_ttl = 30 # seconds - _db_health_cache = {'value': {}, 'time': 0} _db_health_ttl = 10 # seconds +# Simple RSS tracking for dashboard +_peak_rss = 0 +_start_rss = 0 + # Worker registry for distributed testing import hashlib import random @@ -669,39 +651,26 @@ def get_system_stats(): stats['proc_rss'] = 0 stats['proc_threads'] = 0 - # Memory leak detection - global _memory_samples, _peak_rss, _start_rss + # Simple RSS tracking for dashboard + global _peak_rss, _start_rss rss = stats.get('proc_rss', 0) if rss > 0: if _start_rss == 0: _start_rss = rss if rss > _peak_rss: _peak_rss = rss - _memory_samples.append((time.time(), rss)) - if len(_memory_samples) > _memory_sample_max: - _memory_samples.pop(0) - stats['proc_rss_peak'] = _peak_rss stats['proc_rss_start'] = _start_rss stats['proc_rss_growth'] = rss - _start_rss if _start_rss > 0 else 0 - # GC stats for leak detection + # GC generation counts (fast - no object iteration) try: gc_counts = gc.get_count() stats['gc_count_gen0'] = gc_counts[0] stats['gc_count_gen1'] = gc_counts[1] stats['gc_count_gen2'] = gc_counts[2] - # Cache gc.get_objects() - expensive call (~23ms) - global _gc_objects_cache - now = time.time() - if now - _gc_objects_cache['time'] > _gc_objects_ttl: - _gc_objects_cache['value'] = len(gc.get_objects()) - _gc_objects_cache['time'] = now - stats['gc_objects'] = _gc_objects_cache['value'] - except Exception as e: - _log('gc stats error: %s' % e, 'debug') + except Exception: stats['gc_count_gen0'] = stats['gc_count_gen1'] = stats['gc_count_gen2'] = 0 - stats['gc_objects'] = 0 return stats @@ -1438,76 +1407,6 @@ class ProxyAPIServer(threading.Thread): return json.dumps({'count': row[0] if row else 0}), 'application/json', 200 except Exception as e: return json.dumps({'error': str(e)}), 'application/json', 500 - elif path == '/api/memory': - # Memory profiling endpoint (localhost only) - if not is_localhost(remote_addr): - return json.dumps({'error': 'not available'}), 'application/json', 404 - try: - mem = {} - - # Process memory from /proc/self/status - try: - with open('/proc/self/status', 'r') as f: - for line in f: - if line.startswith('Vm'): - parts = line.split() - key = parts[0].rstrip(':') - mem[key] = int(parts[1]) * 1024 # Convert to bytes - except IOError: - pass - - # GC stats - gc_stats = { - 'collections': gc.get_count(), - 'threshold': gc.get_threshold(), - 'objects': len(gc.get_objects()), - } - - # Object type counts (top 20) - type_counts = {} - for obj in gc.get_objects(): - t = type(obj).__name__ - type_counts[t] = type_counts.get(t, 0) + 1 - top_types = sorted(type_counts.items(), key=lambda x: -x[1])[:20] - - # Memory samples history - samples = [] - for ts, rss in _memory_samples[-30:]: - samples.append({'time': int(ts), 'rss': rss}) - - result = { - 'process': mem, - 'gc': gc_stats, - 'top_types': [{'type': t, 'count': c} for t, c in top_types], - 'samples': samples, - 'peak_rss': _peak_rss, - 'start_rss': _start_rss, - 'has_objgraph': _has_objgraph, - 'has_pympler': _has_pympler, - } - - # Objgraph most common types (if available) - if _has_objgraph: - try: - result['objgraph_common'] = objgraph.most_common_types(limit=15) - except Exception as e: - result['objgraph_error'] = str(e) - - # Pympler summary (if available) - if _has_pympler: - try: - all_objects = muppy.get_objects() - sum_table = summary.summarize(all_objects) - result['pympler_summary'] = [ - {'type': row[0], 'count': row[1], 'size': row[2]} - for row in sum_table[:20] - ] - except Exception as e: - result['pympler_error'] = str(e) - - return json.dumps(result, indent=2), 'application/json', 200 - except Exception as e: - return json.dumps({'error': str(e)}), 'application/json', 500 elif path == '/health': return json.dumps({'status': 'ok', 'version': __version__, 'timestamp': int(time.time())}), 'application/json', 200