├── .formatter.exs ├── .github └── FUNDING.yml ├── .gitignore ├── README.md ├── config └── config.exs ├── docker-compose.yml ├── docker ├── grafana │ ├── .env │ ├── dashboards │ │ ├── beam_dashboard.json │ │ ├── dashboards.yml │ │ ├── postgres_dashboard.json │ │ └── rabbitmq.json │ └── datasources │ │ └── datasource.yml ├── prometheus │ └── config.yml └── rabbitmq │ ├── plugins │ └── rabbitmq.conf ├── lib ├── elixir_popularity.ex ├── elixir_popularity │ ├── application.ex │ └── repo.ex ├── hackernews_api.ex ├── hackernews_id_processor.ex ├── hackernews_item.ex ├── hackernews_payload_processor.ex ├── hn_id_generator.ex ├── hn_stats.ex └── rmq_publisher.ex ├── mix.exs ├── mix.lock ├── priv └── repo │ └── migrations │ └── 20191205052229_create_stats_table.exs └── test ├── elixir_popularity_test.exs └── test_helper.exs /.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [akoutmos] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where third-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | elixir_popularity-*.tar 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ElixirPopularity 2 | 3 | **TODO: Add description** 4 | 5 | ## Installation 6 | 7 | If [available in Hex](https://hex.pm/docs/publish), the package can be installed 8 | by adding `elixir_popularity` to your list of dependencies in `mix.exs`: 9 | 10 | ```elixir 11 | def deps do 12 | [ 13 | {:elixir_popularity, "~> 0.1.0"} 14 | ] 15 | end 16 | ``` 17 | 18 | Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) 19 | and published on [HexDocs](https://hexdocs.pm). Once published, the docs can 20 | be found at [https://hexdocs.pm/elixir_popularity](https://hexdocs.pm/elixir_popularity). 21 | 22 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | config :elixir_popularity, ecto_repos: [ElixirPopularity.Repo] 4 | 5 | config :elixir_popularity, ElixirPopularity.Repo, 6 | database: "elixir_popularity_repo", 7 | username: "postgres", 8 | password: "postgres", 9 | hostname: "localhost", 10 | log: false 11 | 12 | config :logger, :console, format: "[$level] $message\n" 13 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | 3 | services: 4 | cadvisor: 5 | image: google/cadvisor:v0.33.0 6 | ports: 7 | - '8080:8080' 8 | volumes: 9 | - /:/rootfs:ro 10 | - /var/run:/var/run:ro 11 | - /sys:/sys:ro 12 | - /var/lib/docker/:/var/lib/docker:ro 13 | 14 | rabbitmq: 15 | image: rabbitmq:3.8 16 | ports: 17 | - '5672:5672' 18 | - '15672:15672' 19 | - '15692:15692' 20 | volumes: 21 | - ./docker/rabbitmq/plugins:/etc/rabbitmq/enabled_plugins 22 | - ./docker/rabbitmq/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro 23 | - rabbitmq-data:/var/lib/rabbitmq 24 | 25 | postgres: 26 | image: postgres:12.0 27 | ports: 28 | - '5432:5432' 29 | volumes: 30 | - postgres-data:/var/lib/postgresql/data 31 | environment: 32 | POSTGRES_PASSWORD: postgres 33 | POSTGRES_USER: postgres 34 | 35 | postgres_exporter: 36 | image: wrouesnel/postgres_exporter:v0.7.0 37 | ports: 38 | - '9187:9187' 39 | depends_on: 40 | - postgres 41 | environment: 42 | DATA_SOURCE_USER: postgres 43 | DATA_SOURCE_PASS: postgres 44 | DATA_SOURCE_URI: postgres:5432/?sslmode=disable 45 | 46 | grafana: 47 | image: grafana/grafana:6.4.4 48 | depends_on: 49 | - prometheus 50 | ports: 51 | - '3000:3000' 52 | volumes: 53 | - grafana-data:/var/lib/grafana 54 | - ./docker/grafana/:/etc/grafana/provisioning/ 55 | env_file: 56 | - ./docker/grafana/.env 57 | 58 | prometheus: 59 | image: prom/prometheus:v2.13.0 60 | ports: 61 | - '9090:9090' 62 | volumes: 63 | - ./docker/prometheus/:/etc/prometheus/ 64 | - prometheus-data:/prometheus 65 | command: 66 | - '--config.file=/etc/prometheus/config.yml' 67 | - '--storage.tsdb.path=/prometheus' 68 | - '--web.console.libraries=/usr/share/prometheus/console_libraries' 69 | - '--web.console.templates=/usr/share/prometheus/consoles' 70 | 71 | volumes: 72 | postgres-data: {} 73 | rabbitmq-data: {} 74 | prometheus-data: {} 75 | grafana-data: {} 76 | -------------------------------------------------------------------------------- /docker/grafana/.env: -------------------------------------------------------------------------------- 1 | GF_SECURITY_ADMIN_PASSWORD=admin 2 | GF_USERS_ALLOW_SIGN_UP=false 3 | -------------------------------------------------------------------------------- /docker/grafana/dashboards/beam_dashboard.json: -------------------------------------------------------------------------------- 1 | { 2 | "__inputs": [ 3 | { 4 | "description": "", 5 | "label": "Prometheus", 6 | "name": "DS_PROMETHEUS", 7 | "pluginId": "prometheus", 8 | "pluginName": "Prometheus", 9 | "type": "datasource" 10 | } 11 | ], 12 | "annotations": { 13 | "list": [ 14 | { 15 | "builtIn": 1, 16 | "datasource": "-- Grafana --", 17 | "enable": true, 18 | "hide": true, 19 | "iconColor": "rgba(0, 211, 255, 1)", 20 | "name": "Annotations & Alerts", 21 | "type": "dashboard" 22 | } 23 | ] 24 | }, 25 | "editable": true, 26 | "gnetId": null, 27 | "graphTooltip": 1, 28 | "hideControls": false, 29 | "id": 10, 30 | "links": [], 31 | "refresh": "5s", 32 | "rows": [ 33 | { 34 | "collapse": false, 35 | "height": "250px", 36 | "panels": [ 37 | { 38 | "aliasColors": { 39 | "ETS Limit": "#508642" 40 | }, 41 | "bars": false, 42 | "dashLength": 10, 43 | "dashes": false, 44 | "datasource": "prometheus", 45 | "fill": 1, 46 | "id": 12, 47 | "legend": { 48 | "avg": false, 49 | "current": false, 50 | "max": false, 51 | "min": false, 52 | "show": true, 53 | "total": false, 54 | "values": false 55 | }, 56 | "lines": true, 57 | "linewidth": 1, 58 | "links": [], 59 | "nullPointMode": "null", 60 | "percentage": false, 61 | "pointradius": 5, 62 | "points": false, 63 | "renderer": "flot", 64 | "seriesOverrides": [ 65 | { 66 | "alias": "DETS Tables", 67 | "yaxis": 1 68 | } 69 | ], 70 | "spaceLength": 10, 71 | "span": 3, 72 | "stack": false, 73 | "steppedLine": false, 74 | "targets": [ 75 | { 76 | "expr": "erlang_vm_ets_limit{instance=\"$node\"}", 77 | "format": "time_series", 78 | "intervalFactor": 2, 79 | "legendFormat": "ETS Limit", 80 | "refId": "A", 81 | "step": 2 82 | }, 83 | { 84 | "expr": "erlang_vm_ets_tables{instance=\"$node\"}", 85 | "format": "time_series", 86 | "intervalFactor": 2, 87 | "legendFormat": "ETS Tables", 88 | "refId": "B", 89 | "step": 2 90 | }, 91 | { 92 | "expr": "erlang_vm_dets_tables{instance=\"$node\"}", 93 | "format": "time_series", 94 | "intervalFactor": 2, 95 | "legendFormat": "DETS Tables", 96 | "refId": "C", 97 | "step": 2 98 | } 99 | ], 100 | "thresholds": [], 101 | "timeFrom": null, 102 | "timeShift": null, 103 | "title": "ETS/DETS", 104 | "tooltip": { 105 | "shared": true, 106 | "sort": 0, 107 | "value_type": "individual" 108 | }, 109 | "type": "graph", 110 | "xaxis": { 111 | "buckets": null, 112 | "mode": "time", 113 | "name": null, 114 | "show": true, 115 | "values": [] 116 | }, 117 | "yaxes": [ 118 | { 119 | "format": "short", 120 | "label": null, 121 | "logBase": 1, 122 | "max": null, 123 | "min": null, 124 | "show": true 125 | }, 126 | { 127 | "format": "short", 128 | "label": null, 129 | "logBase": 1, 130 | "max": null, 131 | "min": null, 132 | "show": true 133 | } 134 | ] 135 | }, 136 | { 137 | "aliasColors": {}, 138 | "bars": false, 139 | "dashLength": 10, 140 | "dashes": false, 141 | "datasource": "prometheus", 142 | "fill": 1, 143 | "id": 14, 144 | "legend": { 145 | "avg": false, 146 | "current": false, 147 | "max": false, 148 | "min": false, 149 | "show": true, 150 | "total": false, 151 | "values": false 152 | }, 153 | "lines": true, 154 | "linewidth": 1, 155 | "links": [], 156 | "nullPointMode": "null", 157 | "percentage": false, 158 | "pointradius": 5, 159 | "points": false, 160 | "renderer": "flot", 161 | "seriesOverrides": [], 162 | "spaceLength": 10, 163 | "span": 3, 164 | "stack": false, 165 | "steppedLine": false, 166 | "targets": [ 167 | { 168 | "expr": "erlang_vm_process_limit{instance=\"$node\"}", 169 | "format": "time_series", 170 | "hide": true, 171 | "intervalFactor": 2, 172 | "legendFormat": "Process Limit", 173 | "refId": "A", 174 | "step": 2 175 | }, 176 | { 177 | "expr": "erlang_vm_process_count{instance=\"$node\"}", 178 | "format": "time_series", 179 | "intervalFactor": 2, 180 | "legendFormat": "Processes", 181 | "refId": "B", 182 | "step": 2 183 | }, 184 | { 185 | "expr": "erlang_vm_statistics_run_queues_length_total{instance=\"$node\"}", 186 | "format": "time_series", 187 | "intervalFactor": 2, 188 | "legendFormat": "Run Queues Length", 189 | "refId": "C", 190 | "step": 2 191 | } 192 | ], 193 | "thresholds": [], 194 | "timeFrom": null, 195 | "timeShift": null, 196 | "title": "Processes", 197 | "tooltip": { 198 | "shared": true, 199 | "sort": 0, 200 | "value_type": "individual" 201 | }, 202 | "type": "graph", 203 | "xaxis": { 204 | "buckets": null, 205 | "mode": "time", 206 | "name": null, 207 | "show": true, 208 | "values": [] 209 | }, 210 | "yaxes": [ 211 | { 212 | "format": "short", 213 | "label": null, 214 | "logBase": 1, 215 | "max": null, 216 | "min": null, 217 | "show": true 218 | }, 219 | { 220 | "format": "short", 221 | "label": null, 222 | "logBase": 1, 223 | "max": null, 224 | "min": null, 225 | "show": true 226 | } 227 | ] 228 | }, 229 | { 230 | "aliasColors": {}, 231 | "bars": false, 232 | "dashLength": 10, 233 | "dashes": false, 234 | "datasource": "prometheus", 235 | "fill": 1, 236 | "id": 15, 237 | "legend": { 238 | "avg": false, 239 | "current": false, 240 | "max": false, 241 | "min": false, 242 | "show": true, 243 | "total": false, 244 | "values": false 245 | }, 246 | "lines": true, 247 | "linewidth": 1, 248 | "links": [], 249 | "nullPointMode": "null", 250 | "percentage": false, 251 | "pointradius": 5, 252 | "points": false, 253 | "renderer": "flot", 254 | "seriesOverrides": [ 255 | { 256 | "alias": "Reductions", 257 | "yaxis": 2 258 | } 259 | ], 260 | "spaceLength": 10, 261 | "span": 3, 262 | "stack": false, 263 | "steppedLine": false, 264 | "targets": [ 265 | { 266 | "expr": "irate(erlang_vm_statistics_context_switches{instance=\"$node\"}[$interval])", 267 | "format": "time_series", 268 | "intervalFactor": 2, 269 | "legendFormat": "Context Switches", 270 | "refId": "B", 271 | "step": 2 272 | }, 273 | { 274 | "expr": "irate(erlang_vm_statistics_reductions_total{instance=\"$node\"}[$interval])", 275 | "format": "time_series", 276 | "intervalFactor": 2, 277 | "legendFormat": "Reductions", 278 | "refId": "C", 279 | "step": 2 280 | }, 281 | { 282 | "expr": "irate(erlang_vm_statistics_runtime_milliseconds{instance=\"$node\"}[$interval])", 283 | "format": "time_series", 284 | "intervalFactor": 2, 285 | "legendFormat": "Runtime", 286 | "refId": "D", 287 | "step": 2 288 | } 289 | ], 290 | "thresholds": [], 291 | "timeFrom": null, 292 | "timeShift": null, 293 | "title": "Load", 294 | "tooltip": { 295 | "shared": true, 296 | "sort": 0, 297 | "value_type": "individual" 298 | }, 299 | "type": "graph", 300 | "xaxis": { 301 | "buckets": null, 302 | "mode": "time", 303 | "name": null, 304 | "show": true, 305 | "values": [] 306 | }, 307 | "yaxes": [ 308 | { 309 | "format": "short", 310 | "label": null, 311 | "logBase": 1, 312 | "max": null, 313 | "min": null, 314 | "show": true 315 | }, 316 | { 317 | "format": "short", 318 | "label": null, 319 | "logBase": 1, 320 | "max": null, 321 | "min": null, 322 | "show": true 323 | } 324 | ] 325 | }, 326 | { 327 | "cacheTimeout": null, 328 | "colorBackground": false, 329 | "colorValue": false, 330 | "colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], 331 | "datasource": "prometheus", 332 | "decimals": 1, 333 | "format": "dtdurations", 334 | "gauge": { 335 | "maxValue": 100, 336 | "minValue": 0, 337 | "show": false, 338 | "thresholdLabels": false, 339 | "thresholdMarkers": true 340 | }, 341 | "id": 16, 342 | "interval": null, 343 | "links": [], 344 | "mappingType": 1, 345 | "mappingTypes": [ 346 | { 347 | "name": "value to text", 348 | "value": 1 349 | }, 350 | { 351 | "name": "range to text", 352 | "value": 2 353 | } 354 | ], 355 | "maxDataPoints": 100, 356 | "nullPointMode": "connected", 357 | "nullText": null, 358 | "postfix": "", 359 | "postfixFontSize": "50%", 360 | "prefix": "", 361 | "prefixFontSize": "50%", 362 | "rangeMaps": [ 363 | { 364 | "from": "null", 365 | "text": "N/A", 366 | "to": "null" 367 | } 368 | ], 369 | "span": 3, 370 | "sparkline": { 371 | "fillColor": "rgba(31, 118, 189, 0.18)", 372 | "full": false, 373 | "lineColor": "rgb(31, 120, 193)", 374 | "show": false 375 | }, 376 | "tableColumn": "", 377 | "targets": [ 378 | { 379 | "expr": "process_uptime_seconds{instance=\"$node\"}", 380 | "intervalFactor": 2, 381 | "legendFormat": "", 382 | "refId": "A", 383 | "step": 4 384 | } 385 | ], 386 | "thresholds": "", 387 | "title": "Uptime", 388 | "type": "singlestat", 389 | "valueFontSize": "80%", 390 | "valueMaps": [ 391 | { 392 | "op": "=", 393 | "text": "N/A", 394 | "value": "null" 395 | } 396 | ], 397 | "valueName": "avg" 398 | } 399 | ], 400 | "repeat": null, 401 | "repeatIteration": null, 402 | "repeatRowId": null, 403 | "showTitle": false, 404 | "title": "New row", 405 | "titleSize": "h6" 406 | }, 407 | { 408 | "collapse": false, 409 | "height": "250px", 410 | "panels": [ 411 | { 412 | "aliasColors": {}, 413 | "bars": false, 414 | "dashLength": 10, 415 | "dashes": false, 416 | "datasource": "prometheus", 417 | "editable": true, 418 | "error": false, 419 | "fill": 1, 420 | "grid": {}, 421 | "id": 6, 422 | "legend": { 423 | "avg": false, 424 | "current": false, 425 | "max": false, 426 | "min": false, 427 | "show": true, 428 | "total": false, 429 | "values": false 430 | }, 431 | "lines": true, 432 | "linewidth": 2, 433 | "links": [], 434 | "nullPointMode": "connected", 435 | "percentage": false, 436 | "pointradius": 5, 437 | "points": false, 438 | "renderer": "flot", 439 | "seriesOverrides": [], 440 | "spaceLength": 10, 441 | "span": 4, 442 | "stack": false, 443 | "steppedLine": false, 444 | "targets": [ 445 | { 446 | "expr": "irate(erlang_vm_statistics_bytes_output_total{instance=\"$node\"}[$interval])", 447 | "format": "time_series", 448 | "intervalFactor": 2, 449 | "legendFormat": "Output Bytes", 450 | "metric": "erlang_vm_statistics_bytes_output_total", 451 | "refId": "A", 452 | "step": 2 453 | }, 454 | { 455 | "expr": "irate(erlang_vm_statistics_bytes_received_total{instance=\"$node\"}[$interval])", 456 | "format": "time_series", 457 | "intervalFactor": 2, 458 | "legendFormat": "Received Bytes", 459 | "metric": "erlang_vm_statistics_bytes_received_total", 460 | "refId": "B", 461 | "step": 2 462 | } 463 | ], 464 | "thresholds": [], 465 | "timeFrom": null, 466 | "timeShift": null, 467 | "title": "VM IO", 468 | "tooltip": { 469 | "msResolution": false, 470 | "shared": true, 471 | "sort": 0, 472 | "value_type": "cumulative" 473 | }, 474 | "type": "graph", 475 | "xaxis": { 476 | "buckets": null, 477 | "mode": "time", 478 | "name": null, 479 | "show": true, 480 | "values": [] 481 | }, 482 | "yaxes": [ 483 | { 484 | "format": "decbytes", 485 | "logBase": 1, 486 | "max": null, 487 | "min": null, 488 | "show": true 489 | }, 490 | { 491 | "format": "GBs", 492 | "logBase": 1, 493 | "max": null, 494 | "min": null, 495 | "show": false 496 | } 497 | ] 498 | }, 499 | { 500 | "aliasColors": {}, 501 | "bars": false, 502 | "dashLength": 10, 503 | "dashes": false, 504 | "datasource": "prometheus", 505 | "editable": true, 506 | "error": false, 507 | "fill": 1, 508 | "grid": {}, 509 | "id": 7, 510 | "legend": { 511 | "alignAsTable": false, 512 | "avg": false, 513 | "current": false, 514 | "max": false, 515 | "min": false, 516 | "rightSide": false, 517 | "show": true, 518 | "total": false, 519 | "values": false 520 | }, 521 | "lines": true, 522 | "linewidth": 2, 523 | "links": [], 524 | "nullPointMode": "connected", 525 | "percentage": false, 526 | "pointradius": 5, 527 | "points": false, 528 | "renderer": "flot", 529 | "seriesOverrides": [ 530 | { 531 | "alias": "Words Reclaimed", 532 | "yaxis": 2 533 | }, 534 | { 535 | "alias": "Bytes Reclaimed", 536 | "yaxis": 2 537 | } 538 | ], 539 | "spaceLength": 10, 540 | "span": 4, 541 | "stack": false, 542 | "steppedLine": false, 543 | "targets": [ 544 | { 545 | "expr": "irate(erlang_vm_statistics_garbage_collection_number_of_gcs{instance=\"$node\"}[$interval])", 546 | "format": "time_series", 547 | "intervalFactor": 2, 548 | "legendFormat": "Number of GCs", 549 | "metric": "erlang_vm_statistics_garbage_collection_number_of_gcs", 550 | "refId": "A", 551 | "step": 2 552 | }, 553 | { 554 | "expr": "irate(erlang_vm_statistics_garbage_collection_bytes_reclaimed{instance=\"$node\"}[$interval])", 555 | "format": "time_series", 556 | "intervalFactor": 2, 557 | "legendFormat": "Bytes Reclaimed", 558 | "metric": "erlang_vm_statistics_garbage_collection_words_reclaimed", 559 | "refId": "B", 560 | "step": 2 561 | } 562 | ], 563 | "thresholds": [], 564 | "timeFrom": null, 565 | "timeShift": null, 566 | "title": "VM GC", 567 | "tooltip": { 568 | "msResolution": false, 569 | "shared": true, 570 | "sort": 0, 571 | "value_type": "cumulative" 572 | }, 573 | "type": "graph", 574 | "xaxis": { 575 | "buckets": null, 576 | "mode": "time", 577 | "name": null, 578 | "show": true, 579 | "values": [] 580 | }, 581 | "yaxes": [ 582 | { 583 | "format": "short", 584 | "logBase": 1, 585 | "max": null, 586 | "min": null, 587 | "show": true 588 | }, 589 | { 590 | "format": "decbytes", 591 | "logBase": 1, 592 | "max": null, 593 | "min": null, 594 | "show": true 595 | } 596 | ] 597 | }, 598 | { 599 | "aliasColors": {}, 600 | "bars": false, 601 | "dashLength": 10, 602 | "dashes": false, 603 | "datasource": "prometheus", 604 | "editable": true, 605 | "error": false, 606 | "fill": 1, 607 | "grid": {}, 608 | "id": 3, 609 | "legend": { 610 | "avg": false, 611 | "current": false, 612 | "max": false, 613 | "min": false, 614 | "show": true, 615 | "total": false, 616 | "values": false 617 | }, 618 | "lines": true, 619 | "linewidth": 2, 620 | "links": [], 621 | "nullPointMode": "connected", 622 | "percentage": false, 623 | "pointradius": 5, 624 | "points": false, 625 | "renderer": "flot", 626 | "seriesOverrides": [], 627 | "spaceLength": 10, 628 | "span": 4, 629 | "stack": true, 630 | "steppedLine": false, 631 | "targets": [ 632 | { 633 | "expr": "erlang_vm_memory_bytes_total{instance=\"$node\", kind=\"processes\"}", 634 | "format": "time_series", 635 | "intervalFactor": 2, 636 | "legendFormat": "Processes Memory", 637 | "refId": "B", 638 | "step": 2 639 | }, 640 | { 641 | "expr": "erlang_vm_memory_system_bytes_total{instance=\"$node\", usage=\"atom\"}", 642 | "format": "time_series", 643 | "intervalFactor": 2, 644 | "legendFormat": "Atoms", 645 | "refId": "C", 646 | "step": 2 647 | }, 648 | { 649 | "expr": "erlang_vm_memory_system_bytes_total{instance=\"$node\", usage=\"binary\"}", 650 | "format": "time_series", 651 | "intervalFactor": 2, 652 | "legendFormat": "Binary", 653 | "refId": "D", 654 | "step": 2 655 | }, 656 | { 657 | "expr": "erlang_vm_memory_system_bytes_total{instance=\"$node\", usage=\"code\"}", 658 | "format": "time_series", 659 | "intervalFactor": 2, 660 | "legendFormat": "Code", 661 | "refId": "E", 662 | "step": 2 663 | }, 664 | { 665 | "expr": "erlang_vm_memory_system_bytes_total{instance=\"$node\", usage=\"ets\"}", 666 | "format": "time_series", 667 | "intervalFactor": 2, 668 | "legendFormat": "ETS", 669 | "refId": "F", 670 | "step": 2 671 | } 672 | ], 673 | "thresholds": [], 674 | "timeFrom": null, 675 | "timeShift": null, 676 | "title": "VM Memory", 677 | "tooltip": { 678 | "msResolution": false, 679 | "shared": true, 680 | "sort": 0, 681 | "value_type": "individual" 682 | }, 683 | "type": "graph", 684 | "xaxis": { 685 | "buckets": null, 686 | "mode": "time", 687 | "name": null, 688 | "show": true, 689 | "values": [] 690 | }, 691 | "yaxes": [ 692 | { 693 | "format": "bytes", 694 | "logBase": 1, 695 | "max": null, 696 | "min": null, 697 | "show": true 698 | }, 699 | { 700 | "format": "short", 701 | "logBase": 1, 702 | "max": null, 703 | "min": null, 704 | "show": true 705 | } 706 | ] 707 | } 708 | ], 709 | "repeat": null, 710 | "repeatIteration": null, 711 | "repeatRowId": null, 712 | "showTitle": false, 713 | "title": "New row", 714 | "titleSize": "h6" 715 | }, 716 | { 717 | "collapse": false, 718 | "height": "250px", 719 | "panels": [ 720 | { 721 | "aliasColors": {}, 722 | "bars": false, 723 | "dashLength": 10, 724 | "dashes": false, 725 | "datasource": "prometheus", 726 | "editable": true, 727 | "error": false, 728 | "fill": 1, 729 | "grid": {}, 730 | "id": 11, 731 | "legend": { 732 | "avg": false, 733 | "current": false, 734 | "max": false, 735 | "min": false, 736 | "show": true, 737 | "total": false, 738 | "values": false 739 | }, 740 | "lines": true, 741 | "linewidth": 2, 742 | "links": [], 743 | "nullPointMode": "connected", 744 | "percentage": false, 745 | "pointradius": 5, 746 | "points": false, 747 | "renderer": "flot", 748 | "seriesOverrides": [], 749 | "spaceLength": 10, 750 | "span": 4, 751 | "stack": false, 752 | "steppedLine": false, 753 | "targets": [ 754 | { 755 | "expr": "process_virtual_memory_bytes{instance=\"$node\"}", 756 | "format": "time_series", 757 | "intervalFactor": 2, 758 | "legendFormat": "Virtual Memory", 759 | "refId": "A", 760 | "step": 2 761 | }, 762 | { 763 | "expr": "process_resident_memory_bytes{instance=\"$node\"}", 764 | "format": "time_series", 765 | "intervalFactor": 2, 766 | "legendFormat": "Resident Memory", 767 | "refId": "B", 768 | "step": 2 769 | } 770 | ], 771 | "thresholds": [], 772 | "timeFrom": null, 773 | "timeShift": null, 774 | "title": "OS Process Memory", 775 | "tooltip": { 776 | "msResolution": true, 777 | "shared": true, 778 | "sort": 0, 779 | "value_type": "cumulative" 780 | }, 781 | "type": "graph", 782 | "xaxis": { 783 | "buckets": null, 784 | "mode": "time", 785 | "name": null, 786 | "show": true, 787 | "values": [] 788 | }, 789 | "yaxes": [ 790 | { 791 | "format": "bytes", 792 | "label": null, 793 | "logBase": 1, 794 | "max": null, 795 | "min": null, 796 | "show": true 797 | }, 798 | { 799 | "format": "short", 800 | "label": null, 801 | "logBase": 1, 802 | "max": null, 803 | "min": null, 804 | "show": true 805 | } 806 | ] 807 | }, 808 | { 809 | "aliasColors": {}, 810 | "bars": false, 811 | "dashLength": 10, 812 | "dashes": false, 813 | "datasource": "prometheus", 814 | "editable": true, 815 | "error": false, 816 | "fill": 1, 817 | "grid": {}, 818 | "id": 8, 819 | "legend": { 820 | "avg": false, 821 | "current": false, 822 | "max": false, 823 | "min": false, 824 | "show": true, 825 | "total": false, 826 | "values": false 827 | }, 828 | "lines": true, 829 | "linewidth": 2, 830 | "links": [], 831 | "nullPointMode": "connected", 832 | "percentage": false, 833 | "pointradius": 5, 834 | "points": false, 835 | "renderer": "flot", 836 | "seriesOverrides": [ 837 | { 838 | "alias": "Max Ports", 839 | "yaxis": 2 840 | }, 841 | { 842 | "alias": "Ports", 843 | "yaxis": 2 844 | } 845 | ], 846 | "spaceLength": 10, 847 | "span": 4, 848 | "stack": false, 849 | "steppedLine": false, 850 | "targets": [ 851 | { 852 | "expr": "process_open_fds{instance=\"$node\"}", 853 | "format": "time_series", 854 | "intervalFactor": 2, 855 | "legendFormat": "Open FDs", 856 | "metric": "", 857 | "refId": "A", 858 | "step": 2 859 | }, 860 | { 861 | "expr": "process_max_fds{instance=\"$node\"}", 862 | "format": "time_series", 863 | "intervalFactor": 2, 864 | "legendFormat": "Max FDs", 865 | "refId": "B", 866 | "step": 2 867 | }, 868 | { 869 | "expr": "erlang_vm_port_limit{instance=\"$node\"}", 870 | "format": "time_series", 871 | "intervalFactor": 2, 872 | "legendFormat": "Max Ports", 873 | "refId": "C", 874 | "step": 2 875 | }, 876 | { 877 | "expr": "erlang_vm_port_count{instance=\"$node\"}", 878 | "format": "time_series", 879 | "intervalFactor": 2, 880 | "legendFormat": "Ports", 881 | "refId": "D", 882 | "step": 2 883 | } 884 | ], 885 | "thresholds": [], 886 | "timeFrom": null, 887 | "timeShift": null, 888 | "title": "File Descriptors & Ports", 889 | "tooltip": { 890 | "msResolution": true, 891 | "shared": true, 892 | "sort": 0, 893 | "value_type": "cumulative" 894 | }, 895 | "type": "graph", 896 | "xaxis": { 897 | "buckets": null, 898 | "mode": "time", 899 | "name": null, 900 | "show": true, 901 | "values": [] 902 | }, 903 | "yaxes": [ 904 | { 905 | "format": "none", 906 | "label": null, 907 | "logBase": 1, 908 | "max": null, 909 | "min": null, 910 | "show": true 911 | }, 912 | { 913 | "format": "short", 914 | "label": null, 915 | "logBase": 1, 916 | "max": null, 917 | "min": null, 918 | "show": true 919 | } 920 | ] 921 | }, 922 | { 923 | "aliasColors": {}, 924 | "bars": false, 925 | "dashLength": 10, 926 | "dashes": false, 927 | "datasource": "prometheus", 928 | "decimals": null, 929 | "editable": true, 930 | "error": false, 931 | "fill": 1, 932 | "grid": {}, 933 | "id": 10, 934 | "legend": { 935 | "avg": false, 936 | "current": false, 937 | "max": false, 938 | "min": false, 939 | "show": true, 940 | "total": false, 941 | "values": false 942 | }, 943 | "lines": true, 944 | "linewidth": 2, 945 | "links": [], 946 | "nullPointMode": "connected", 947 | "percentage": false, 948 | "pointradius": 5, 949 | "points": false, 950 | "renderer": "flot", 951 | "seriesOverrides": [ 952 | { 953 | "alias": "Threads", 954 | "yaxis": 2 955 | } 956 | ], 957 | "spaceLength": 10, 958 | "span": 4, 959 | "stack": false, 960 | "steppedLine": false, 961 | "targets": [ 962 | { 963 | "expr": "process_threads_total{instance=\"$node\"}", 964 | "format": "time_series", 965 | "intervalFactor": 2, 966 | "legendFormat": "Threads", 967 | "metric": "", 968 | "refId": "A", 969 | "step": 2 970 | }, 971 | { 972 | "expr": "sum(irate(process_cpu_seconds_total{instance=\"$node\"}[$interval])) without (kind) * 100", 973 | "format": "time_series", 974 | "intervalFactor": 2, 975 | "legendFormat": "CPU", 976 | "refId": "B", 977 | "step": 2 978 | } 979 | ], 980 | "thresholds": [], 981 | "timeFrom": null, 982 | "timeShift": null, 983 | "title": "Native Threads & CPU", 984 | "tooltip": { 985 | "msResolution": true, 986 | "shared": true, 987 | "sort": 0, 988 | "value_type": "cumulative" 989 | }, 990 | "type": "graph", 991 | "xaxis": { 992 | "buckets": null, 993 | "mode": "time", 994 | "name": null, 995 | "show": true, 996 | "values": [] 997 | }, 998 | "yaxes": [ 999 | { 1000 | "format": "percent", 1001 | "label": null, 1002 | "logBase": 1, 1003 | "max": null, 1004 | "min": null, 1005 | "show": true 1006 | }, 1007 | { 1008 | "format": "short", 1009 | "label": null, 1010 | "logBase": 1, 1011 | "max": null, 1012 | "min": null, 1013 | "show": true 1014 | } 1015 | ] 1016 | } 1017 | ], 1018 | "repeat": null, 1019 | "repeatIteration": null, 1020 | "repeatRowId": null, 1021 | "showTitle": false, 1022 | "title": "New row", 1023 | "titleSize": "h6" 1024 | } 1025 | ], 1026 | "schemaVersion": 14, 1027 | "style": "dark", 1028 | "tags": [], 1029 | "templating": { 1030 | "list": [ 1031 | { 1032 | "allValue": null, 1033 | "current": { 1034 | "text": "172.17.0.1:4000", 1035 | "value": "172.17.0.1:4000" 1036 | }, 1037 | "datasource": "prometheus", 1038 | "hide": 0, 1039 | "includeAll": false, 1040 | "label": "Node", 1041 | "multi": false, 1042 | "name": "node", 1043 | "options": [], 1044 | "query": "label_values(erlang_vm_process_count, instance)", 1045 | "refresh": 1, 1046 | "regex": "", 1047 | "sort": 1, 1048 | "tagValuesQuery": "label_values({job=\"$tag\"},instance)", 1049 | "tags": ["api", "elli", "linux", "lisp", "mysql", "prometheus", "push", "rabbitmq"], 1050 | "tagsQuery": "label_values(job)", 1051 | "type": "query", 1052 | "useTags": true 1053 | }, 1054 | { 1055 | "auto": false, 1056 | "auto_count": 30, 1057 | "auto_min": "10s", 1058 | "current": { 1059 | "text": "5m", 1060 | "value": "5m" 1061 | }, 1062 | "hide": 0, 1063 | "label": null, 1064 | "name": "interval", 1065 | "options": [ 1066 | { 1067 | "selected": false, 1068 | "text": "1m", 1069 | "value": "1m" 1070 | }, 1071 | { 1072 | "selected": true, 1073 | "text": "5m", 1074 | "value": "5m" 1075 | }, 1076 | { 1077 | "selected": false, 1078 | "text": "10m", 1079 | "value": "10m" 1080 | }, 1081 | { 1082 | "selected": false, 1083 | "text": "30m", 1084 | "value": "30m" 1085 | }, 1086 | { 1087 | "selected": false, 1088 | "text": "1h", 1089 | "value": "1h" 1090 | }, 1091 | { 1092 | "selected": false, 1093 | "text": "6h", 1094 | "value": "6h" 1095 | }, 1096 | { 1097 | "selected": false, 1098 | "text": "12h", 1099 | "value": "12h" 1100 | }, 1101 | { 1102 | "selected": false, 1103 | "text": "1d", 1104 | "value": "1d" 1105 | }, 1106 | { 1107 | "selected": false, 1108 | "text": "7d", 1109 | "value": "7d" 1110 | }, 1111 | { 1112 | "selected": false, 1113 | "text": "14d", 1114 | "value": "14d" 1115 | }, 1116 | { 1117 | "selected": false, 1118 | "text": "30d", 1119 | "value": "30d" 1120 | } 1121 | ], 1122 | "query": "1m,5m,10m,30m,1h,6h,12h,1d,7d,14d,30d", 1123 | "refresh": 2, 1124 | "type": "interval" 1125 | } 1126 | ] 1127 | }, 1128 | "time": { 1129 | "from": "now-1h", 1130 | "to": "now" 1131 | }, 1132 | "timepicker": { 1133 | "now": true, 1134 | "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], 1135 | "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] 1136 | }, 1137 | "timezone": "browser", 1138 | "title": "BEAM Stats", 1139 | "version": 41, 1140 | "description": "Dashboard for BEAM VM internal Statistics." 1141 | } 1142 | -------------------------------------------------------------------------------- /docker/grafana/dashboards/dashboards.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: 'Sample Dashboards' 5 | type: 'file' 6 | disableDeletion: true 7 | editable: false 8 | updateIntervalSeconds: 10 9 | options: 10 | folder: '/etc/grafana/provisioning/dashboards/' 11 | -------------------------------------------------------------------------------- /docker/grafana/dashboards/postgres_dashboard.json: -------------------------------------------------------------------------------- 1 | { 2 | "annotations": { 3 | "list": [ 4 | { 5 | "builtIn": 1, 6 | "datasource": "-- Grafana --", 7 | "enable": true, 8 | "hide": true, 9 | "iconColor": "rgba(0, 211, 255, 1)", 10 | "name": "Annotations & Alerts", 11 | "type": "dashboard" 12 | } 13 | ] 14 | }, 15 | "description": "Dashboard for PostgreSQL Statistics.", 16 | "editable": true, 17 | "gnetId": 6742, 18 | "graphTooltip": 1, 19 | "iteration": 1566404735785, 20 | "links": [], 21 | "panels": [ 22 | { 23 | "collapsed": false, 24 | "gridPos": { 25 | "h": 1, 26 | "w": 24, 27 | "x": 0, 28 | "y": 0 29 | }, 30 | "id": 34, 31 | "panels": [], 32 | "title": "Settings", 33 | "type": "row" 34 | }, 35 | { 36 | "cacheTimeout": null, 37 | "colorBackground": false, 38 | "colorValue": true, 39 | "colors": ["#299c46", "#7eb26d", "#d44a3a"], 40 | "datasource": "prometheus", 41 | "format": "none", 42 | "gauge": { 43 | "maxValue": 100, 44 | "minValue": 0, 45 | "show": false, 46 | "thresholdLabels": false, 47 | "thresholdMarkers": true 48 | }, 49 | "gridPos": { 50 | "h": 3, 51 | "w": 3, 52 | "x": 0, 53 | "y": 1 54 | }, 55 | "id": 2, 56 | "interval": null, 57 | "links": [], 58 | "mappingType": 1, 59 | "mappingTypes": [ 60 | { 61 | "name": "value to text", 62 | "value": 1 63 | }, 64 | { 65 | "name": "range to text", 66 | "value": 2 67 | } 68 | ], 69 | "maxDataPoints": 100, 70 | "nullPointMode": "connected", 71 | "nullText": null, 72 | "options": {}, 73 | "postfix": "", 74 | "postfixFontSize": "50%", 75 | "prefix": "", 76 | "prefixFontSize": "50%", 77 | "rangeMaps": [ 78 | { 79 | "from": "null", 80 | "text": "N/A", 81 | "to": "null" 82 | } 83 | ], 84 | "sparkline": { 85 | "fillColor": "rgba(31, 118, 189, 0.18)", 86 | "full": false, 87 | "lineColor": "rgb(31, 120, 193)", 88 | "show": false 89 | }, 90 | "tableColumn": "", 91 | "targets": [ 92 | { 93 | "expr": "pg_static", 94 | "format": "time_series", 95 | "intervalFactor": 1, 96 | "legendFormat": "{{short_version}}", 97 | "refId": "A" 98 | } 99 | ], 100 | "thresholds": "", 101 | "title": "Version", 102 | "type": "singlestat", 103 | "valueFontSize": "80%", 104 | "valueMaps": [ 105 | { 106 | "op": "=", 107 | "text": "N/A", 108 | "value": "null" 109 | } 110 | ], 111 | "valueName": "name" 112 | }, 113 | { 114 | "cacheTimeout": null, 115 | "colorBackground": false, 116 | "colorValue": false, 117 | "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], 118 | "datasource": "prometheus", 119 | "format": "none", 120 | "gauge": { 121 | "maxValue": 100, 122 | "minValue": 0, 123 | "show": false, 124 | "thresholdLabels": false, 125 | "thresholdMarkers": true 126 | }, 127 | "gridPos": { 128 | "h": 3, 129 | "w": 3, 130 | "x": 3, 131 | "y": 1 132 | }, 133 | "id": 54, 134 | "interval": null, 135 | "links": [], 136 | "mappingType": 1, 137 | "mappingTypes": [ 138 | { 139 | "name": "value to text", 140 | "value": 1 141 | }, 142 | { 143 | "name": "range to text", 144 | "value": 2 145 | } 146 | ], 147 | "maxDataPoints": 100, 148 | "nullPointMode": "connected", 149 | "nullText": null, 150 | "options": {}, 151 | "postfix": "", 152 | "postfixFontSize": "50%", 153 | "prefix": "", 154 | "prefixFontSize": "50%", 155 | "rangeMaps": [ 156 | { 157 | "from": "null", 158 | "text": "N/A", 159 | "to": "null" 160 | } 161 | ], 162 | "sparkline": { 163 | "fillColor": "rgba(31, 118, 189, 0.18)", 164 | "full": false, 165 | "lineColor": "rgb(31, 120, 193)", 166 | "show": false 167 | }, 168 | "tableColumn": "", 169 | "targets": [ 170 | { 171 | "expr": "pg_settings_max_connections{instance=\"$instance\"}", 172 | "format": "time_series", 173 | "intervalFactor": 1, 174 | "refId": "A" 175 | } 176 | ], 177 | "thresholds": "", 178 | "title": "Max Connections", 179 | "type": "singlestat", 180 | "valueFontSize": "80%", 181 | "valueMaps": [ 182 | { 183 | "op": "=", 184 | "text": "N/A", 185 | "value": "null" 186 | } 187 | ], 188 | "valueName": "avg" 189 | }, 190 | { 191 | "cacheTimeout": null, 192 | "colorBackground": false, 193 | "colorValue": false, 194 | "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], 195 | "datasource": "prometheus", 196 | "format": "bytes", 197 | "gauge": { 198 | "maxValue": 100, 199 | "minValue": 0, 200 | "show": false, 201 | "thresholdLabels": false, 202 | "thresholdMarkers": true 203 | }, 204 | "gridPos": { 205 | "h": 3, 206 | "w": 4, 207 | "x": 6, 208 | "y": 1 209 | }, 210 | "id": 56, 211 | "interval": null, 212 | "links": [], 213 | "mappingType": 1, 214 | "mappingTypes": [ 215 | { 216 | "name": "value to text", 217 | "value": 1 218 | }, 219 | { 220 | "name": "range to text", 221 | "value": 2 222 | } 223 | ], 224 | "maxDataPoints": 100, 225 | "nullPointMode": "connected", 226 | "nullText": null, 227 | "options": {}, 228 | "postfix": "", 229 | "postfixFontSize": "50%", 230 | "prefix": "", 231 | "prefixFontSize": "50%", 232 | "rangeMaps": [ 233 | { 234 | "from": "null", 235 | "text": "N/A", 236 | "to": "null" 237 | } 238 | ], 239 | "sparkline": { 240 | "fillColor": "rgba(31, 118, 189, 0.18)", 241 | "full": false, 242 | "lineColor": "rgb(31, 120, 193)", 243 | "show": false 244 | }, 245 | "tableColumn": "", 246 | "targets": [ 247 | { 248 | "expr": "pg_settings_shared_buffers_bytes{instance=\"$instance\"}", 249 | "format": "time_series", 250 | "intervalFactor": 1, 251 | "refId": "A" 252 | } 253 | ], 254 | "thresholds": "", 255 | "title": "Shared Buffers", 256 | "type": "singlestat", 257 | "valueFontSize": "80%", 258 | "valueMaps": [ 259 | { 260 | "op": "=", 261 | "text": "N/A", 262 | "value": "null" 263 | } 264 | ], 265 | "valueName": "current" 266 | }, 267 | { 268 | "cacheTimeout": null, 269 | "colorBackground": false, 270 | "colorValue": false, 271 | "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], 272 | "datasource": "prometheus", 273 | "format": "bytes", 274 | "gauge": { 275 | "maxValue": 100, 276 | "minValue": 0, 277 | "show": false, 278 | "thresholdLabels": false, 279 | "thresholdMarkers": true 280 | }, 281 | "gridPos": { 282 | "h": 3, 283 | "w": 4, 284 | "x": 10, 285 | "y": 1 286 | }, 287 | "id": 58, 288 | "interval": null, 289 | "links": [], 290 | "mappingType": 1, 291 | "mappingTypes": [ 292 | { 293 | "name": "value to text", 294 | "value": 1 295 | }, 296 | { 297 | "name": "range to text", 298 | "value": 2 299 | } 300 | ], 301 | "maxDataPoints": 100, 302 | "nullPointMode": "connected", 303 | "nullText": null, 304 | "options": {}, 305 | "postfix": "", 306 | "postfixFontSize": "50%", 307 | "prefix": "", 308 | "prefixFontSize": "50%", 309 | "rangeMaps": [ 310 | { 311 | "from": "null", 312 | "text": "N/A", 313 | "to": "null" 314 | } 315 | ], 316 | "sparkline": { 317 | "fillColor": "rgba(31, 118, 189, 0.18)", 318 | "full": false, 319 | "lineColor": "rgb(31, 120, 193)", 320 | "show": false 321 | }, 322 | "tableColumn": "", 323 | "targets": [ 324 | { 325 | "expr": "pg_settings_effective_cache_size_bytes{instance=\"$instance\"}", 326 | "format": "time_series", 327 | "intervalFactor": 1, 328 | "refId": "A" 329 | } 330 | ], 331 | "thresholds": "", 332 | "title": "Effective Cache", 333 | "type": "singlestat", 334 | "valueFontSize": "80%", 335 | "valueMaps": [ 336 | { 337 | "op": "=", 338 | "text": "N/A", 339 | "value": "null" 340 | } 341 | ], 342 | "valueName": "current" 343 | }, 344 | { 345 | "cacheTimeout": null, 346 | "colorBackground": false, 347 | "colorValue": false, 348 | "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], 349 | "datasource": "prometheus", 350 | "format": "bytes", 351 | "gauge": { 352 | "maxValue": 100, 353 | "minValue": 0, 354 | "show": false, 355 | "thresholdLabels": false, 356 | "thresholdMarkers": true 357 | }, 358 | "gridPos": { 359 | "h": 3, 360 | "w": 4, 361 | "x": 14, 362 | "y": 1 363 | }, 364 | "id": 60, 365 | "interval": null, 366 | "links": [], 367 | "mappingType": 1, 368 | "mappingTypes": [ 369 | { 370 | "name": "value to text", 371 | "value": 1 372 | }, 373 | { 374 | "name": "range to text", 375 | "value": 2 376 | } 377 | ], 378 | "maxDataPoints": 100, 379 | "nullPointMode": "connected", 380 | "nullText": null, 381 | "options": {}, 382 | "postfix": "", 383 | "postfixFontSize": "50%", 384 | "prefix": "", 385 | "prefixFontSize": "50%", 386 | "rangeMaps": [ 387 | { 388 | "from": "null", 389 | "text": "N/A", 390 | "to": "null" 391 | } 392 | ], 393 | "sparkline": { 394 | "fillColor": "rgba(31, 118, 189, 0.18)", 395 | "full": false, 396 | "lineColor": "rgb(31, 120, 193)", 397 | "show": false 398 | }, 399 | "tableColumn": "", 400 | "targets": [ 401 | { 402 | "expr": "pg_settings_maintenance_work_mem_bytes{instance=\"$instance\"}", 403 | "format": "time_series", 404 | "intervalFactor": 1, 405 | "refId": "A" 406 | } 407 | ], 408 | "thresholds": "", 409 | "title": "Maintenance Work Mem", 410 | "type": "singlestat", 411 | "valueFontSize": "80%", 412 | "valueMaps": [ 413 | { 414 | "op": "=", 415 | "text": "N/A", 416 | "value": "null" 417 | } 418 | ], 419 | "valueName": "current" 420 | }, 421 | { 422 | "cacheTimeout": null, 423 | "colorBackground": false, 424 | "colorValue": false, 425 | "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], 426 | "datasource": "prometheus", 427 | "format": "bytes", 428 | "gauge": { 429 | "maxValue": 100, 430 | "minValue": 0, 431 | "show": false, 432 | "thresholdLabels": false, 433 | "thresholdMarkers": true 434 | }, 435 | "gridPos": { 436 | "h": 3, 437 | "w": 3, 438 | "x": 18, 439 | "y": 1 440 | }, 441 | "id": 66, 442 | "interval": null, 443 | "links": [], 444 | "mappingType": 1, 445 | "mappingTypes": [ 446 | { 447 | "name": "value to text", 448 | "value": 1 449 | }, 450 | { 451 | "name": "range to text", 452 | "value": 2 453 | } 454 | ], 455 | "maxDataPoints": 100, 456 | "nullPointMode": "connected", 457 | "nullText": null, 458 | "options": {}, 459 | "postfix": "", 460 | "postfixFontSize": "50%", 461 | "prefix": "", 462 | "prefixFontSize": "50%", 463 | "rangeMaps": [ 464 | { 465 | "from": "null", 466 | "text": "N/A", 467 | "to": "null" 468 | } 469 | ], 470 | "sparkline": { 471 | "fillColor": "rgba(31, 118, 189, 0.18)", 472 | "full": false, 473 | "lineColor": "rgb(31, 120, 193)", 474 | "show": false 475 | }, 476 | "tableColumn": "", 477 | "targets": [ 478 | { 479 | "expr": "pg_settings_work_mem_bytes{instance=\"$instance\"}", 480 | "format": "time_series", 481 | "intervalFactor": 1, 482 | "legendFormat": "", 483 | "refId": "A" 484 | } 485 | ], 486 | "thresholds": "", 487 | "title": "Work Mem", 488 | "type": "singlestat", 489 | "valueFontSize": "80%", 490 | "valueMaps": [ 491 | { 492 | "op": "=", 493 | "text": "N/A", 494 | "value": "null" 495 | } 496 | ], 497 | "valueName": "current" 498 | }, 499 | { 500 | "cacheTimeout": null, 501 | "colorBackground": false, 502 | "colorValue": false, 503 | "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], 504 | "datasource": "prometheus", 505 | "decimals": 1, 506 | "format": "bytes", 507 | "gauge": { 508 | "maxValue": 100, 509 | "minValue": 0, 510 | "show": false, 511 | "thresholdLabels": false, 512 | "thresholdMarkers": true 513 | }, 514 | "gridPos": { 515 | "h": 3, 516 | "w": 3, 517 | "x": 21, 518 | "y": 1 519 | }, 520 | "id": 32, 521 | "interval": null, 522 | "links": [], 523 | "mappingType": 1, 524 | "mappingTypes": [ 525 | { 526 | "name": "value to text", 527 | "value": 1 528 | }, 529 | { 530 | "name": "range to text", 531 | "value": 2 532 | } 533 | ], 534 | "maxDataPoints": 100, 535 | "nullPointMode": "connected", 536 | "nullText": null, 537 | "options": {}, 538 | "postfix": "", 539 | "postfixFontSize": "50%", 540 | "prefix": "", 541 | "prefixFontSize": "50%", 542 | "rangeMaps": [ 543 | { 544 | "from": "null", 545 | "text": "N/A", 546 | "to": "null" 547 | } 548 | ], 549 | "sparkline": { 550 | "fillColor": "rgba(31, 118, 189, 0.18)", 551 | "full": false, 552 | "lineColor": "rgb(31, 120, 193)", 553 | "show": false 554 | }, 555 | "tableColumn": "", 556 | "targets": [ 557 | { 558 | "expr": "pg_settings_max_wal_size_bytes{instance=\"$instance\"}", 559 | "format": "time_series", 560 | "intervalFactor": 1, 561 | "refId": "A" 562 | } 563 | ], 564 | "thresholds": "", 565 | "title": "Max WAL Size", 566 | "type": "singlestat", 567 | "valueFontSize": "80%", 568 | "valueMaps": [ 569 | { 570 | "op": "=", 571 | "text": "N/A", 572 | "value": "null" 573 | } 574 | ], 575 | "valueName": "current" 576 | }, 577 | { 578 | "cacheTimeout": null, 579 | "colorBackground": false, 580 | "colorValue": false, 581 | "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], 582 | "datasource": "prometheus", 583 | "format": "none", 584 | "gauge": { 585 | "maxValue": 100, 586 | "minValue": 0, 587 | "show": false, 588 | "thresholdLabels": false, 589 | "thresholdMarkers": true 590 | }, 591 | "gridPos": { 592 | "h": 3, 593 | "w": 3, 594 | "x": 0, 595 | "y": 4 596 | }, 597 | "id": 62, 598 | "interval": null, 599 | "links": [], 600 | "mappingType": 1, 601 | "mappingTypes": [ 602 | { 603 | "name": "value to text", 604 | "value": 1 605 | }, 606 | { 607 | "name": "range to text", 608 | "value": 2 609 | } 610 | ], 611 | "maxDataPoints": 100, 612 | "nullPointMode": "connected", 613 | "nullText": null, 614 | "options": {}, 615 | "postfix": "", 616 | "postfixFontSize": "50%", 617 | "prefix": "", 618 | "prefixFontSize": "50%", 619 | "rangeMaps": [ 620 | { 621 | "from": "null", 622 | "text": "N/A", 623 | "to": "null" 624 | } 625 | ], 626 | "sparkline": { 627 | "fillColor": "rgba(31, 118, 189, 0.18)", 628 | "full": false, 629 | "lineColor": "rgb(31, 120, 193)", 630 | "show": false 631 | }, 632 | "tableColumn": "", 633 | "targets": [ 634 | { 635 | "expr": "pg_settings_random_page_cost{instance=\"$instance\"}", 636 | "format": "time_series", 637 | "intervalFactor": 1, 638 | "refId": "A" 639 | } 640 | ], 641 | "thresholds": "", 642 | "title": "Random Page Cost", 643 | "type": "singlestat", 644 | "valueFontSize": "80%", 645 | "valueMaps": [ 646 | { 647 | "op": "=", 648 | "text": "N/A", 649 | "value": "null" 650 | } 651 | ], 652 | "valueName": "current" 653 | }, 654 | { 655 | "cacheTimeout": null, 656 | "colorBackground": false, 657 | "colorValue": false, 658 | "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], 659 | "datasource": "prometheus", 660 | "format": "none", 661 | "gauge": { 662 | "maxValue": 100, 663 | "minValue": 0, 664 | "show": false, 665 | "thresholdLabels": false, 666 | "thresholdMarkers": true 667 | }, 668 | "gridPos": { 669 | "h": 3, 670 | "w": 3, 671 | "x": 3, 672 | "y": 4 673 | }, 674 | "id": 70, 675 | "interval": null, 676 | "links": [], 677 | "mappingType": 1, 678 | "mappingTypes": [ 679 | { 680 | "name": "value to text", 681 | "value": 1 682 | }, 683 | { 684 | "name": "range to text", 685 | "value": 2 686 | } 687 | ], 688 | "maxDataPoints": 100, 689 | "nullPointMode": "connected", 690 | "nullText": null, 691 | "options": {}, 692 | "postfix": "", 693 | "postfixFontSize": "50%", 694 | "prefix": "", 695 | "prefixFontSize": "50%", 696 | "rangeMaps": [ 697 | { 698 | "from": "null", 699 | "text": "N/A", 700 | "to": "null" 701 | } 702 | ], 703 | "sparkline": { 704 | "fillColor": "rgba(31, 118, 189, 0.18)", 705 | "full": false, 706 | "lineColor": "rgb(31, 120, 193)", 707 | "show": false 708 | }, 709 | "tableColumn": "", 710 | "targets": [ 711 | { 712 | "expr": "pg_settings_seq_page_cost", 713 | "format": "time_series", 714 | "intervalFactor": 1, 715 | "refId": "A" 716 | } 717 | ], 718 | "thresholds": "", 719 | "title": "Seq Page Cost", 720 | "type": "singlestat", 721 | "valueFontSize": "80%", 722 | "valueMaps": [ 723 | { 724 | "op": "=", 725 | "text": "N/A", 726 | "value": "null" 727 | } 728 | ], 729 | "valueName": "current" 730 | }, 731 | { 732 | "cacheTimeout": null, 733 | "colorBackground": false, 734 | "colorValue": false, 735 | "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], 736 | "datasource": "prometheus", 737 | "format": "none", 738 | "gauge": { 739 | "maxValue": 100, 740 | "minValue": 0, 741 | "show": false, 742 | "thresholdLabels": false, 743 | "thresholdMarkers": true 744 | }, 745 | "gridPos": { 746 | "h": 3, 747 | "w": 4, 748 | "x": 6, 749 | "y": 4 750 | }, 751 | "id": 64, 752 | "interval": null, 753 | "links": [], 754 | "mappingType": 1, 755 | "mappingTypes": [ 756 | { 757 | "name": "value to text", 758 | "value": 1 759 | }, 760 | { 761 | "name": "range to text", 762 | "value": 2 763 | } 764 | ], 765 | "maxDataPoints": 100, 766 | "nullPointMode": "connected", 767 | "nullText": null, 768 | "options": {}, 769 | "postfix": "", 770 | "postfixFontSize": "50%", 771 | "prefix": "", 772 | "prefixFontSize": "50%", 773 | "rangeMaps": [ 774 | { 775 | "from": "null", 776 | "text": "N/A", 777 | "to": "null" 778 | } 779 | ], 780 | "sparkline": { 781 | "fillColor": "rgba(31, 118, 189, 0.18)", 782 | "full": false, 783 | "lineColor": "rgb(31, 120, 193)", 784 | "show": false 785 | }, 786 | "tableColumn": "", 787 | "targets": [ 788 | { 789 | "expr": "pg_settings_max_worker_processes{instance=\"$instance\"}", 790 | "format": "time_series", 791 | "intervalFactor": 1, 792 | "refId": "A" 793 | } 794 | ], 795 | "thresholds": "", 796 | "title": "Max Worker Processes", 797 | "type": "singlestat", 798 | "valueFontSize": "80%", 799 | "valueMaps": [ 800 | { 801 | "op": "=", 802 | "text": "N/A", 803 | "value": "null" 804 | } 805 | ], 806 | "valueName": "avg" 807 | }, 808 | { 809 | "cacheTimeout": null, 810 | "colorBackground": false, 811 | "colorValue": false, 812 | "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], 813 | "datasource": "prometheus", 814 | "format": "none", 815 | "gauge": { 816 | "maxValue": 100, 817 | "minValue": 0, 818 | "show": false, 819 | "thresholdLabels": false, 820 | "thresholdMarkers": true 821 | }, 822 | "gridPos": { 823 | "h": 3, 824 | "w": 4, 825 | "x": 10, 826 | "y": 4 827 | }, 828 | "id": 68, 829 | "interval": null, 830 | "links": [], 831 | "mappingType": 1, 832 | "mappingTypes": [ 833 | { 834 | "name": "value to text", 835 | "value": 1 836 | }, 837 | { 838 | "name": "range to text", 839 | "value": 2 840 | } 841 | ], 842 | "maxDataPoints": 100, 843 | "nullPointMode": "connected", 844 | "nullText": null, 845 | "options": {}, 846 | "postfix": "", 847 | "postfixFontSize": "50%", 848 | "prefix": "", 849 | "prefixFontSize": "50%", 850 | "rangeMaps": [ 851 | { 852 | "from": "null", 853 | "text": "N/A", 854 | "to": "null" 855 | } 856 | ], 857 | "sparkline": { 858 | "fillColor": "rgba(31, 118, 189, 0.18)", 859 | "full": false, 860 | "lineColor": "rgb(31, 120, 193)", 861 | "show": false 862 | }, 863 | "tableColumn": "", 864 | "targets": [ 865 | { 866 | "expr": "pg_settings_max_parallel_workers{instance=\"$instance\"}", 867 | "format": "time_series", 868 | "intervalFactor": 1, 869 | "refId": "A" 870 | } 871 | ], 872 | "thresholds": "", 873 | "title": "Max Parallel Workers", 874 | "type": "singlestat", 875 | "valueFontSize": "80%", 876 | "valueMaps": [ 877 | { 878 | "op": "=", 879 | "text": "N/A", 880 | "value": "null" 881 | } 882 | ], 883 | "valueName": "current" 884 | }, 885 | { 886 | "collapsed": false, 887 | "gridPos": { 888 | "h": 1, 889 | "w": 24, 890 | "x": 0, 891 | "y": 7 892 | }, 893 | "id": 36, 894 | "panels": [], 895 | "title": "Connection / Transaction Statistics", 896 | "type": "row" 897 | }, 898 | { 899 | "aliasColors": {}, 900 | "bars": false, 901 | "dashLength": 10, 902 | "dashes": false, 903 | "datasource": "prometheus", 904 | "fill": 1, 905 | "fillGradient": 0, 906 | "gridPos": { 907 | "h": 10, 908 | "w": 12, 909 | "x": 0, 910 | "y": 8 911 | }, 912 | "id": 6, 913 | "legend": { 914 | "alignAsTable": true, 915 | "avg": true, 916 | "current": true, 917 | "hideEmpty": true, 918 | "hideZero": true, 919 | "max": true, 920 | "min": true, 921 | "show": true, 922 | "total": false, 923 | "values": true 924 | }, 925 | "lines": true, 926 | "linewidth": 1, 927 | "links": [], 928 | "nullPointMode": "null", 929 | "options": { 930 | "dataLinks": [] 931 | }, 932 | "percentage": false, 933 | "pointradius": 5, 934 | "points": false, 935 | "renderer": "flot", 936 | "seriesOverrides": [], 937 | "spaceLength": 10, 938 | "stack": true, 939 | "steppedLine": false, 940 | "targets": [ 941 | { 942 | "expr": "pg_stat_activity_count{instance=\"$instance\", datname=\"$db\"}", 943 | "format": "time_series", 944 | "intervalFactor": 1, 945 | "legendFormat": "{{state}}", 946 | "refId": "A" 947 | } 948 | ], 949 | "thresholds": [], 950 | "timeFrom": null, 951 | "timeRegions": [], 952 | "timeShift": null, 953 | "title": "Connections", 954 | "tooltip": { 955 | "shared": true, 956 | "sort": 2, 957 | "value_type": "individual" 958 | }, 959 | "type": "graph", 960 | "xaxis": { 961 | "buckets": null, 962 | "mode": "time", 963 | "name": null, 964 | "show": true, 965 | "values": [] 966 | }, 967 | "yaxes": [ 968 | { 969 | "format": "short", 970 | "label": null, 971 | "logBase": 1, 972 | "max": null, 973 | "min": null, 974 | "show": true 975 | }, 976 | { 977 | "format": "short", 978 | "label": null, 979 | "logBase": 1, 980 | "max": null, 981 | "min": null, 982 | "show": true 983 | } 984 | ], 985 | "yaxis": { 986 | "align": false, 987 | "alignLevel": null 988 | } 989 | }, 990 | { 991 | "aliasColors": {}, 992 | "bars": false, 993 | "dashLength": 10, 994 | "dashes": false, 995 | "datasource": "prometheus", 996 | "fill": 1, 997 | "fillGradient": 0, 998 | "gridPos": { 999 | "h": 10, 1000 | "w": 12, 1001 | "x": 12, 1002 | "y": 8 1003 | }, 1004 | "id": 8, 1005 | "legend": { 1006 | "alignAsTable": false, 1007 | "avg": false, 1008 | "current": false, 1009 | "max": false, 1010 | "min": false, 1011 | "show": true, 1012 | "total": false, 1013 | "values": false 1014 | }, 1015 | "lines": true, 1016 | "linewidth": 1, 1017 | "links": [], 1018 | "nullPointMode": "null", 1019 | "options": { 1020 | "dataLinks": [] 1021 | }, 1022 | "percentage": false, 1023 | "pointradius": 5, 1024 | "points": false, 1025 | "renderer": "flot", 1026 | "seriesOverrides": [], 1027 | "spaceLength": 10, 1028 | "stack": false, 1029 | "steppedLine": false, 1030 | "targets": [ 1031 | { 1032 | "expr": "irate(pg_stat_database_xact_commit{instance=\"$instance\", datname=\"$db\"}[5m])", 1033 | "format": "time_series", 1034 | "intervalFactor": 1, 1035 | "legendFormat": "commits", 1036 | "refId": "A" 1037 | }, 1038 | { 1039 | "expr": "irate(pg_stat_database_xact_rollback{instance=\"$instance\", datname=\"$db\"}[5m])", 1040 | "format": "time_series", 1041 | "intervalFactor": 1, 1042 | "legendFormat": "rollbacks", 1043 | "refId": "B" 1044 | } 1045 | ], 1046 | "thresholds": [], 1047 | "timeFrom": null, 1048 | "timeRegions": [], 1049 | "timeShift": null, 1050 | "title": "Transactions", 1051 | "tooltip": { 1052 | "shared": true, 1053 | "sort": 0, 1054 | "value_type": "individual" 1055 | }, 1056 | "type": "graph", 1057 | "xaxis": { 1058 | "buckets": null, 1059 | "mode": "time", 1060 | "name": null, 1061 | "show": true, 1062 | "values": [] 1063 | }, 1064 | "yaxes": [ 1065 | { 1066 | "format": "short", 1067 | "label": null, 1068 | "logBase": 1, 1069 | "max": null, 1070 | "min": null, 1071 | "show": true 1072 | }, 1073 | { 1074 | "format": "short", 1075 | "label": null, 1076 | "logBase": 1, 1077 | "max": null, 1078 | "min": null, 1079 | "show": true 1080 | } 1081 | ], 1082 | "yaxis": { 1083 | "align": false, 1084 | "alignLevel": null 1085 | } 1086 | }, 1087 | { 1088 | "aliasColors": {}, 1089 | "bars": false, 1090 | "dashLength": 10, 1091 | "dashes": false, 1092 | "datasource": "prometheus", 1093 | "fill": 1, 1094 | "fillGradient": 0, 1095 | "gridPos": { 1096 | "h": 8, 1097 | "w": 12, 1098 | "x": 0, 1099 | "y": 18 1100 | }, 1101 | "id": 18, 1102 | "legend": { 1103 | "alignAsTable": true, 1104 | "avg": true, 1105 | "current": true, 1106 | "max": true, 1107 | "min": true, 1108 | "show": true, 1109 | "total": false, 1110 | "values": true 1111 | }, 1112 | "lines": true, 1113 | "linewidth": 1, 1114 | "links": [], 1115 | "nullPointMode": "null", 1116 | "options": { 1117 | "dataLinks": [] 1118 | }, 1119 | "percentage": false, 1120 | "pointradius": 5, 1121 | "points": false, 1122 | "renderer": "flot", 1123 | "seriesOverrides": [], 1124 | "spaceLength": 10, 1125 | "stack": false, 1126 | "steppedLine": false, 1127 | "targets": [ 1128 | { 1129 | "expr": "irate(pg_stat_database_tup_fetched{instance=\"$instance\", datname=\"$db\"}[5m])", 1130 | "format": "time_series", 1131 | "intervalFactor": 1, 1132 | "legendFormat": "SELECT (index scan)", 1133 | "refId": "A" 1134 | }, 1135 | { 1136 | "expr": "irate(pg_stat_database_tup_returned{instance=\"$instance\", datname=\"$db\"}[5m])", 1137 | "format": "time_series", 1138 | "intervalFactor": 1, 1139 | "legendFormat": "SELECT (table scan)", 1140 | "refId": "B" 1141 | } 1142 | ], 1143 | "thresholds": [], 1144 | "timeFrom": null, 1145 | "timeRegions": [], 1146 | "timeShift": null, 1147 | "title": "Read Stats", 1148 | "tooltip": { 1149 | "shared": true, 1150 | "sort": 0, 1151 | "value_type": "individual" 1152 | }, 1153 | "type": "graph", 1154 | "xaxis": { 1155 | "buckets": null, 1156 | "mode": "time", 1157 | "name": null, 1158 | "show": true, 1159 | "values": [] 1160 | }, 1161 | "yaxes": [ 1162 | { 1163 | "decimals": null, 1164 | "format": "short", 1165 | "label": "Rows", 1166 | "logBase": 1, 1167 | "max": null, 1168 | "min": null, 1169 | "show": true 1170 | }, 1171 | { 1172 | "format": "short", 1173 | "label": null, 1174 | "logBase": 1, 1175 | "max": null, 1176 | "min": null, 1177 | "show": true 1178 | } 1179 | ], 1180 | "yaxis": { 1181 | "align": false, 1182 | "alignLevel": null 1183 | } 1184 | }, 1185 | { 1186 | "aliasColors": {}, 1187 | "bars": false, 1188 | "dashLength": 10, 1189 | "dashes": false, 1190 | "datasource": "prometheus", 1191 | "fill": 1, 1192 | "fillGradient": 0, 1193 | "gridPos": { 1194 | "h": 8, 1195 | "w": 12, 1196 | "x": 12, 1197 | "y": 18 1198 | }, 1199 | "id": 20, 1200 | "legend": { 1201 | "alignAsTable": true, 1202 | "avg": true, 1203 | "current": true, 1204 | "max": true, 1205 | "min": true, 1206 | "show": true, 1207 | "total": false, 1208 | "values": true 1209 | }, 1210 | "lines": true, 1211 | "linewidth": 1, 1212 | "links": [], 1213 | "nullPointMode": "null", 1214 | "options": { 1215 | "dataLinks": [] 1216 | }, 1217 | "percentage": false, 1218 | "pointradius": 5, 1219 | "points": false, 1220 | "renderer": "flot", 1221 | "seriesOverrides": [], 1222 | "spaceLength": 10, 1223 | "stack": false, 1224 | "steppedLine": false, 1225 | "targets": [ 1226 | { 1227 | "expr": "irate(pg_stat_database_tup_inserted{instance=\"$instance\", datname=\"$db\"}[5m])", 1228 | "format": "time_series", 1229 | "intervalFactor": 1, 1230 | "legendFormat": "INSERT", 1231 | "refId": "A" 1232 | }, 1233 | { 1234 | "expr": "irate(pg_stat_database_tup_updated{instance=\"$instance\", datname=\"$db\"}[5m])", 1235 | "format": "time_series", 1236 | "intervalFactor": 1, 1237 | "legendFormat": "UPDATE", 1238 | "refId": "B" 1239 | }, 1240 | { 1241 | "expr": "irate(pg_stat_database_tup_deleted{instance=\"$instance\", datname=\"$db\"}[5m])", 1242 | "format": "time_series", 1243 | "intervalFactor": 1, 1244 | "legendFormat": "DELETE", 1245 | "refId": "C" 1246 | } 1247 | ], 1248 | "thresholds": [], 1249 | "timeFrom": null, 1250 | "timeRegions": [], 1251 | "timeShift": null, 1252 | "title": "Change Stats", 1253 | "tooltip": { 1254 | "shared": true, 1255 | "sort": 0, 1256 | "value_type": "individual" 1257 | }, 1258 | "type": "graph", 1259 | "xaxis": { 1260 | "buckets": null, 1261 | "mode": "time", 1262 | "name": null, 1263 | "show": true, 1264 | "values": [] 1265 | }, 1266 | "yaxes": [ 1267 | { 1268 | "format": "short", 1269 | "label": "Rows", 1270 | "logBase": 1, 1271 | "max": null, 1272 | "min": null, 1273 | "show": true 1274 | }, 1275 | { 1276 | "format": "short", 1277 | "label": null, 1278 | "logBase": 1, 1279 | "max": null, 1280 | "min": null, 1281 | "show": true 1282 | } 1283 | ], 1284 | "yaxis": { 1285 | "align": false, 1286 | "alignLevel": null 1287 | } 1288 | }, 1289 | { 1290 | "aliasColors": {}, 1291 | "bars": false, 1292 | "dashLength": 10, 1293 | "dashes": false, 1294 | "datasource": "prometheus", 1295 | "fill": 1, 1296 | "fillGradient": 0, 1297 | "gridPos": { 1298 | "h": 8, 1299 | "w": 12, 1300 | "x": 0, 1301 | "y": 26 1302 | }, 1303 | "id": 42, 1304 | "legend": { 1305 | "alignAsTable": true, 1306 | "avg": true, 1307 | "current": true, 1308 | "hideEmpty": true, 1309 | "hideZero": true, 1310 | "max": true, 1311 | "min": true, 1312 | "show": true, 1313 | "total": false, 1314 | "values": true 1315 | }, 1316 | "lines": true, 1317 | "linewidth": 1, 1318 | "links": [], 1319 | "nullPointMode": "null", 1320 | "options": { 1321 | "dataLinks": [] 1322 | }, 1323 | "percentage": false, 1324 | "pointradius": 5, 1325 | "points": false, 1326 | "renderer": "flot", 1327 | "seriesOverrides": [], 1328 | "spaceLength": 10, 1329 | "stack": false, 1330 | "steppedLine": false, 1331 | "targets": [ 1332 | { 1333 | "expr": "pg_stat_activity_max_tx_duration{instance=\"$instance\", datname=\"$db\"}", 1334 | "format": "time_series", 1335 | "intervalFactor": 1, 1336 | "legendFormat": "max_tx_duration [{{state}}]", 1337 | "refId": "A" 1338 | } 1339 | ], 1340 | "thresholds": [], 1341 | "timeFrom": null, 1342 | "timeRegions": [], 1343 | "timeShift": null, 1344 | "title": "Longest Transaction", 1345 | "tooltip": { 1346 | "shared": true, 1347 | "sort": 0, 1348 | "value_type": "individual" 1349 | }, 1350 | "type": "graph", 1351 | "xaxis": { 1352 | "buckets": null, 1353 | "mode": "time", 1354 | "name": null, 1355 | "show": true, 1356 | "values": [] 1357 | }, 1358 | "yaxes": [ 1359 | { 1360 | "format": "s", 1361 | "label": null, 1362 | "logBase": 1, 1363 | "max": null, 1364 | "min": null, 1365 | "show": true 1366 | }, 1367 | { 1368 | "format": "short", 1369 | "label": null, 1370 | "logBase": 1, 1371 | "max": null, 1372 | "min": null, 1373 | "show": true 1374 | } 1375 | ], 1376 | "yaxis": { 1377 | "align": false, 1378 | "alignLevel": null 1379 | } 1380 | }, 1381 | { 1382 | "aliasColors": {}, 1383 | "bars": false, 1384 | "dashLength": 10, 1385 | "dashes": false, 1386 | "datasource": "prometheus", 1387 | "fill": 1, 1388 | "fillGradient": 0, 1389 | "gridPos": { 1390 | "h": 8, 1391 | "w": 12, 1392 | "x": 12, 1393 | "y": 26 1394 | }, 1395 | "id": 44, 1396 | "legend": { 1397 | "avg": false, 1398 | "current": false, 1399 | "max": false, 1400 | "min": false, 1401 | "show": false, 1402 | "total": false, 1403 | "values": false 1404 | }, 1405 | "lines": true, 1406 | "linewidth": 1, 1407 | "links": [], 1408 | "nullPointMode": "null", 1409 | "options": { 1410 | "dataLinks": [] 1411 | }, 1412 | "percentage": false, 1413 | "pointradius": 5, 1414 | "points": false, 1415 | "renderer": "flot", 1416 | "seriesOverrides": [], 1417 | "spaceLength": 10, 1418 | "stack": false, 1419 | "steppedLine": false, 1420 | "targets": [ 1421 | { 1422 | "expr": "pg_stat_database_blks_hit{instance=\"$instance\", datname=\"$db\"} / (pg_stat_database_blks_read{instance=\"$instance\", datname=\"$db\"} + pg_stat_database_blks_hit{instance=\"$instance\", datname=\"$db\"})", 1423 | "format": "time_series", 1424 | "intervalFactor": 1, 1425 | "legendFormat": "Cache Hit Rate", 1426 | "refId": "A" 1427 | } 1428 | ], 1429 | "thresholds": [], 1430 | "timeFrom": null, 1431 | "timeRegions": [], 1432 | "timeShift": null, 1433 | "title": "Cache Hit Rate", 1434 | "tooltip": { 1435 | "shared": true, 1436 | "sort": 0, 1437 | "value_type": "individual" 1438 | }, 1439 | "type": "graph", 1440 | "xaxis": { 1441 | "buckets": null, 1442 | "mode": "time", 1443 | "name": null, 1444 | "show": true, 1445 | "values": [] 1446 | }, 1447 | "yaxes": [ 1448 | { 1449 | "decimals": 4, 1450 | "format": "percentunit", 1451 | "label": "", 1452 | "logBase": 1, 1453 | "max": null, 1454 | "min": null, 1455 | "show": true 1456 | }, 1457 | { 1458 | "format": "short", 1459 | "label": null, 1460 | "logBase": 1, 1461 | "max": null, 1462 | "min": null, 1463 | "show": true 1464 | } 1465 | ], 1466 | "yaxis": { 1467 | "align": false, 1468 | "alignLevel": null 1469 | } 1470 | }, 1471 | { 1472 | "collapsed": false, 1473 | "gridPos": { 1474 | "h": 1, 1475 | "w": 24, 1476 | "x": 0, 1477 | "y": 34 1478 | }, 1479 | "id": 50, 1480 | "panels": [], 1481 | "title": "misc", 1482 | "type": "row" 1483 | }, 1484 | { 1485 | "aliasColors": {}, 1486 | "bars": false, 1487 | "dashLength": 10, 1488 | "dashes": false, 1489 | "datasource": "prometheus", 1490 | "fill": 1, 1491 | "fillGradient": 0, 1492 | "gridPos": { 1493 | "h": 6, 1494 | "w": 17, 1495 | "x": 0, 1496 | "y": 35 1497 | }, 1498 | "id": 46, 1499 | "legend": { 1500 | "alignAsTable": true, 1501 | "avg": true, 1502 | "current": true, 1503 | "max": true, 1504 | "min": true, 1505 | "rightSide": true, 1506 | "show": true, 1507 | "total": false, 1508 | "values": true 1509 | }, 1510 | "lines": true, 1511 | "linewidth": 1, 1512 | "links": [], 1513 | "nullPointMode": "null", 1514 | "options": { 1515 | "dataLinks": [] 1516 | }, 1517 | "percentage": false, 1518 | "pointradius": 5, 1519 | "points": false, 1520 | "renderer": "flot", 1521 | "seriesOverrides": [], 1522 | "spaceLength": 10, 1523 | "stack": false, 1524 | "steppedLine": false, 1525 | "targets": [ 1526 | { 1527 | "expr": "irate(pg_stat_bgwriter_buffers_backend{instance=\"$instance\"}[5m])", 1528 | "format": "time_series", 1529 | "intervalFactor": 1, 1530 | "legendFormat": "buffers_backend", 1531 | "refId": "A" 1532 | }, 1533 | { 1534 | "expr": "irate(pg_stat_bgwriter_buffers_alloc{instance=\"$instance\"}[5m])", 1535 | "format": "time_series", 1536 | "intervalFactor": 1, 1537 | "legendFormat": "buffers_alloc", 1538 | "refId": "B" 1539 | }, 1540 | { 1541 | "expr": "irate(pg_stat_bgwriter_buffers_backend_fsync{instance=\"$instance\"}[5m])", 1542 | "format": "time_series", 1543 | "intervalFactor": 1, 1544 | "legendFormat": "backend_fsync", 1545 | "refId": "C" 1546 | }, 1547 | { 1548 | "expr": "irate(pg_stat_bgwriter_buffers_checkpoint{instance=\"$instance\"}[5m])", 1549 | "format": "time_series", 1550 | "intervalFactor": 1, 1551 | "legendFormat": "buffers_checkpoint", 1552 | "refId": "D" 1553 | }, 1554 | { 1555 | "expr": "irate(pg_stat_bgwriter_buffers_clean{instance=\"$instance\"}[5m])", 1556 | "format": "time_series", 1557 | "intervalFactor": 1, 1558 | "legendFormat": "buffers_clean", 1559 | "refId": "E" 1560 | } 1561 | ], 1562 | "thresholds": [], 1563 | "timeFrom": null, 1564 | "timeRegions": [], 1565 | "timeShift": null, 1566 | "title": "Buffers (bgwriter)", 1567 | "tooltip": { 1568 | "shared": true, 1569 | "sort": 0, 1570 | "value_type": "individual" 1571 | }, 1572 | "type": "graph", 1573 | "xaxis": { 1574 | "buckets": null, 1575 | "mode": "time", 1576 | "name": null, 1577 | "show": true, 1578 | "values": [] 1579 | }, 1580 | "yaxes": [ 1581 | { 1582 | "format": "short", 1583 | "label": null, 1584 | "logBase": 1, 1585 | "max": null, 1586 | "min": null, 1587 | "show": true 1588 | }, 1589 | { 1590 | "format": "short", 1591 | "label": null, 1592 | "logBase": 1, 1593 | "max": null, 1594 | "min": null, 1595 | "show": true 1596 | } 1597 | ], 1598 | "yaxis": { 1599 | "align": false, 1600 | "alignLevel": null 1601 | } 1602 | }, 1603 | { 1604 | "aliasColors": {}, 1605 | "bars": false, 1606 | "dashLength": 10, 1607 | "dashes": false, 1608 | "datasource": "prometheus", 1609 | "fill": 1, 1610 | "fillGradient": 0, 1611 | "gridPos": { 1612 | "h": 6, 1613 | "w": 7, 1614 | "x": 17, 1615 | "y": 35 1616 | }, 1617 | "id": 28, 1618 | "legend": { 1619 | "avg": false, 1620 | "current": false, 1621 | "max": false, 1622 | "min": false, 1623 | "show": true, 1624 | "total": false, 1625 | "values": false 1626 | }, 1627 | "lines": true, 1628 | "linewidth": 1, 1629 | "links": [], 1630 | "nullPointMode": "null", 1631 | "options": { 1632 | "dataLinks": [] 1633 | }, 1634 | "percentage": false, 1635 | "pointradius": 5, 1636 | "points": false, 1637 | "renderer": "flot", 1638 | "seriesOverrides": [], 1639 | "spaceLength": 10, 1640 | "stack": false, 1641 | "steppedLine": false, 1642 | "targets": [ 1643 | { 1644 | "expr": "irate(pg_stat_database_conflicts{instance=\"$instance\", datname=\"$db\"}[5m])", 1645 | "format": "time_series", 1646 | "intervalFactor": 1, 1647 | "legendFormat": "conflicts", 1648 | "refId": "B" 1649 | }, 1650 | { 1651 | "expr": "irate(pg_stat_database_deadlocks{instance=\"$instance\", datname=\"$db\"}[5m])", 1652 | "format": "time_series", 1653 | "intervalFactor": 1, 1654 | "legendFormat": "deadlocks", 1655 | "refId": "A" 1656 | } 1657 | ], 1658 | "thresholds": [], 1659 | "timeFrom": null, 1660 | "timeRegions": [], 1661 | "timeShift": null, 1662 | "title": "Conflicts/Deadlocks", 1663 | "tooltip": { 1664 | "shared": true, 1665 | "sort": 0, 1666 | "value_type": "individual" 1667 | }, 1668 | "type": "graph", 1669 | "xaxis": { 1670 | "buckets": null, 1671 | "mode": "time", 1672 | "name": null, 1673 | "show": true, 1674 | "values": [] 1675 | }, 1676 | "yaxes": [ 1677 | { 1678 | "format": "short", 1679 | "label": null, 1680 | "logBase": 1, 1681 | "max": null, 1682 | "min": null, 1683 | "show": true 1684 | }, 1685 | { 1686 | "format": "short", 1687 | "label": null, 1688 | "logBase": 1, 1689 | "max": null, 1690 | "min": null, 1691 | "show": true 1692 | } 1693 | ], 1694 | "yaxis": { 1695 | "align": false, 1696 | "alignLevel": null 1697 | } 1698 | }, 1699 | { 1700 | "aliasColors": {}, 1701 | "bars": false, 1702 | "dashLength": 10, 1703 | "dashes": false, 1704 | "datasource": "prometheus", 1705 | "decimals": 0, 1706 | "fill": 1, 1707 | "fillGradient": 0, 1708 | "gridPos": { 1709 | "h": 7, 1710 | "w": 17, 1711 | "x": 0, 1712 | "y": 41 1713 | }, 1714 | "id": 30, 1715 | "legend": { 1716 | "alignAsTable": true, 1717 | "avg": true, 1718 | "current": true, 1719 | "max": true, 1720 | "min": true, 1721 | "rightSide": true, 1722 | "show": true, 1723 | "total": false, 1724 | "values": true 1725 | }, 1726 | "lines": true, 1727 | "linewidth": 1, 1728 | "links": [], 1729 | "nullPointMode": "null", 1730 | "options": { 1731 | "dataLinks": [] 1732 | }, 1733 | "percentage": false, 1734 | "pointradius": 5, 1735 | "points": false, 1736 | "renderer": "flot", 1737 | "seriesOverrides": [], 1738 | "spaceLength": 10, 1739 | "stack": false, 1740 | "steppedLine": false, 1741 | "targets": [ 1742 | { 1743 | "expr": "pg_locks_count{instance=\"$instance\", datname=\"$db\"}", 1744 | "format": "time_series", 1745 | "intervalFactor": 1, 1746 | "legendFormat": "{{mode}}", 1747 | "refId": "A" 1748 | } 1749 | ], 1750 | "thresholds": [], 1751 | "timeFrom": null, 1752 | "timeRegions": [], 1753 | "timeShift": null, 1754 | "title": "Lock Tables", 1755 | "tooltip": { 1756 | "shared": true, 1757 | "sort": 0, 1758 | "value_type": "individual" 1759 | }, 1760 | "type": "graph", 1761 | "xaxis": { 1762 | "buckets": null, 1763 | "mode": "time", 1764 | "name": null, 1765 | "show": true, 1766 | "values": [] 1767 | }, 1768 | "yaxes": [ 1769 | { 1770 | "decimals": 0, 1771 | "format": "short", 1772 | "label": null, 1773 | "logBase": 1, 1774 | "max": null, 1775 | "min": null, 1776 | "show": true 1777 | }, 1778 | { 1779 | "format": "short", 1780 | "label": null, 1781 | "logBase": 1, 1782 | "max": null, 1783 | "min": null, 1784 | "show": true 1785 | } 1786 | ], 1787 | "yaxis": { 1788 | "align": false, 1789 | "alignLevel": null 1790 | } 1791 | }, 1792 | { 1793 | "aliasColors": {}, 1794 | "bars": false, 1795 | "dashLength": 10, 1796 | "dashes": false, 1797 | "datasource": "prometheus", 1798 | "description": "Total amount of data written to temporary files by queries in this database. All temporary files are counted, regardless of why the temporary file was created, and regardless of the log_temp_files setting.", 1799 | "fill": 1, 1800 | "fillGradient": 0, 1801 | "gridPos": { 1802 | "h": 7, 1803 | "w": 7, 1804 | "x": 17, 1805 | "y": 41 1806 | }, 1807 | "id": 40, 1808 | "legend": { 1809 | "avg": false, 1810 | "current": false, 1811 | "max": false, 1812 | "min": false, 1813 | "show": false, 1814 | "total": false, 1815 | "values": false 1816 | }, 1817 | "lines": true, 1818 | "linewidth": 1, 1819 | "links": [], 1820 | "nullPointMode": "null", 1821 | "options": { 1822 | "dataLinks": [] 1823 | }, 1824 | "percentage": false, 1825 | "pointradius": 5, 1826 | "points": false, 1827 | "renderer": "flot", 1828 | "seriesOverrides": [], 1829 | "spaceLength": 10, 1830 | "stack": false, 1831 | "steppedLine": false, 1832 | "targets": [ 1833 | { 1834 | "expr": "irate(pg_stat_database_temp_bytes{instance=\"$instance\", datname=\"$db\"}[5m])", 1835 | "format": "time_series", 1836 | "intervalFactor": 1, 1837 | "legendFormat": "Temp Bytes", 1838 | "refId": "A" 1839 | } 1840 | ], 1841 | "thresholds": [], 1842 | "timeFrom": null, 1843 | "timeRegions": [], 1844 | "timeShift": null, 1845 | "title": "Temp File", 1846 | "tooltip": { 1847 | "shared": true, 1848 | "sort": 0, 1849 | "value_type": "individual" 1850 | }, 1851 | "type": "graph", 1852 | "xaxis": { 1853 | "buckets": null, 1854 | "mode": "time", 1855 | "name": null, 1856 | "show": true, 1857 | "values": [] 1858 | }, 1859 | "yaxes": [ 1860 | { 1861 | "format": "bytes", 1862 | "label": null, 1863 | "logBase": 1, 1864 | "max": null, 1865 | "min": null, 1866 | "show": true 1867 | }, 1868 | { 1869 | "format": "short", 1870 | "label": null, 1871 | "logBase": 1, 1872 | "max": null, 1873 | "min": null, 1874 | "show": true 1875 | } 1876 | ], 1877 | "yaxis": { 1878 | "align": false, 1879 | "alignLevel": null 1880 | } 1881 | }, 1882 | { 1883 | "aliasColors": {}, 1884 | "bars": false, 1885 | "dashLength": 10, 1886 | "dashes": false, 1887 | "datasource": "prometheus", 1888 | "fill": 1, 1889 | "fillGradient": 0, 1890 | "gridPos": { 1891 | "h": 9, 1892 | "w": 24, 1893 | "x": 0, 1894 | "y": 48 1895 | }, 1896 | "id": 38, 1897 | "legend": { 1898 | "alignAsTable": true, 1899 | "avg": true, 1900 | "current": true, 1901 | "max": true, 1902 | "min": true, 1903 | "show": true, 1904 | "total": false, 1905 | "values": true 1906 | }, 1907 | "lines": true, 1908 | "linewidth": 1, 1909 | "links": [], 1910 | "nullPointMode": "null", 1911 | "options": { 1912 | "dataLinks": [] 1913 | }, 1914 | "percentage": false, 1915 | "pointradius": 5, 1916 | "points": false, 1917 | "renderer": "flot", 1918 | "seriesOverrides": [], 1919 | "spaceLength": 10, 1920 | "stack": false, 1921 | "steppedLine": false, 1922 | "targets": [ 1923 | { 1924 | "expr": "irate(pg_stat_bgwriter_checkpoint_write_time{instance=\"$instance\"}[5m])", 1925 | "format": "time_series", 1926 | "intervalFactor": 1, 1927 | "legendFormat": "write_time - Total amount of time that has been spent in the portion of checkpoint processing where files are written to disk.", 1928 | "refId": "B" 1929 | }, 1930 | { 1931 | "expr": "irate(pg_stat_bgwriter_checkpoint_sync_time{instance=\"$instance\"}[5m])", 1932 | "format": "time_series", 1933 | "intervalFactor": 1, 1934 | "legendFormat": "sync_time - Total amount of time that has been spent in the portion of checkpoint processing where files are synchronized to disk.", 1935 | "refId": "A" 1936 | } 1937 | ], 1938 | "thresholds": [], 1939 | "timeFrom": null, 1940 | "timeRegions": [], 1941 | "timeShift": null, 1942 | "title": "Checkpoint Stats", 1943 | "tooltip": { 1944 | "shared": true, 1945 | "sort": 0, 1946 | "value_type": "individual" 1947 | }, 1948 | "type": "graph", 1949 | "xaxis": { 1950 | "buckets": null, 1951 | "mode": "time", 1952 | "name": null, 1953 | "show": true, 1954 | "values": [] 1955 | }, 1956 | "yaxes": [ 1957 | { 1958 | "format": "ms", 1959 | "label": null, 1960 | "logBase": 1, 1961 | "max": null, 1962 | "min": null, 1963 | "show": true 1964 | }, 1965 | { 1966 | "format": "short", 1967 | "label": null, 1968 | "logBase": 1, 1969 | "max": null, 1970 | "min": null, 1971 | "show": true 1972 | } 1973 | ], 1974 | "yaxis": { 1975 | "align": false, 1976 | "alignLevel": null 1977 | } 1978 | } 1979 | ], 1980 | "refresh": "5s", 1981 | "schemaVersion": 19, 1982 | "style": "dark", 1983 | "tags": [], 1984 | "templating": { 1985 | "list": [ 1986 | { 1987 | "allValue": null, 1988 | "current": { 1989 | "text": "postgres_exporter:9187", 1990 | "value": "postgres_exporter:9187" 1991 | }, 1992 | "datasource": "prometheus", 1993 | "definition": "", 1994 | "hide": 0, 1995 | "includeAll": false, 1996 | "label": "Instance", 1997 | "multi": false, 1998 | "name": "instance", 1999 | "options": [], 2000 | "query": "label_values(pg_up, instance)", 2001 | "refresh": 1, 2002 | "regex": "", 2003 | "skipUrlSync": false, 2004 | "sort": 1, 2005 | "tagValuesQuery": "", 2006 | "tags": [], 2007 | "tagsQuery": "", 2008 | "type": "query", 2009 | "useTags": false 2010 | }, 2011 | { 2012 | "allValue": null, 2013 | "current": { 2014 | "text": "elixir_monitoring_prom_dev", 2015 | "value": "elixir_monitoring_prom_dev" 2016 | }, 2017 | "datasource": "prometheus", 2018 | "definition": "", 2019 | "hide": 0, 2020 | "includeAll": false, 2021 | "label": "Database", 2022 | "multi": false, 2023 | "name": "db", 2024 | "options": [], 2025 | "query": "label_values(pg_stat_database_tup_fetched{datname!~\"template.*|postgres\"},datname)", 2026 | "refresh": 1, 2027 | "regex": "", 2028 | "skipUrlSync": false, 2029 | "sort": 1, 2030 | "tagValuesQuery": "", 2031 | "tags": [], 2032 | "tagsQuery": "", 2033 | "type": "query", 2034 | "useTags": false 2035 | } 2036 | ] 2037 | }, 2038 | "time": { 2039 | "from": "now-1h", 2040 | "to": "now" 2041 | }, 2042 | "timepicker": { 2043 | "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], 2044 | "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] 2045 | }, 2046 | "timezone": "", 2047 | "title": "PostgreSQL Stats", 2048 | "uid": "XlTsIjdZk", 2049 | "version": 1 2050 | } 2051 | -------------------------------------------------------------------------------- /docker/grafana/datasources/datasource.yml: -------------------------------------------------------------------------------- 1 | # config file version 2 | apiVersion: 1 3 | 4 | # list of datasources that should be deleted from the database 5 | deleteDatasources: 6 | - name: prometheus 7 | orgId: 1 8 | 9 | # list of datasources to insert/update depending 10 | # whats available in the database 11 | datasources: 12 | # name of the datasource. Required 13 | - name: prometheus 14 | # datasource type. Required 15 | type: prometheus 16 | # access mode. direct or proxy. Required 17 | access: proxy 18 | # org id. will default to orgId 1 if not specified 19 | orgId: 1 20 | # url 21 | url: http://prometheus:9090 22 | # database password, if used 23 | password: 24 | # database user, if used 25 | user: 26 | # database name, if used 27 | database: 28 | # enable/disable basic auth 29 | basicAuth: true 30 | # basic auth username 31 | basicAuthUser: admin 32 | # basic auth password 33 | basicAuthPassword: admin 34 | # enable/disable with credentials headers 35 | withCredentials: 36 | # mark as default datasource. Max one per org 37 | isDefault: 38 | # fields that will be converted to json and stored in json_data 39 | jsonData: 40 | graphiteVersion: "1.1" 41 | tlsAuth: false 42 | tlsAuthWithCACert: false 43 | # json object of data that will be encrypted. 44 | secureJsonData: 45 | tlsCACert: "..." 46 | tlsClientCert: "..." 47 | tlsClientKey: "..." 48 | version: 1 49 | # allow users to edit datasources from the UI. 50 | editable: true 51 | -------------------------------------------------------------------------------- /docker/prometheus/config.yml: -------------------------------------------------------------------------------- 1 | # Global Configurations 2 | global: 3 | scrape_interval: 5s 4 | evaluation_interval: 5s 5 | 6 | external_labels: 7 | monitor: 'elixir_sample_app' 8 | 9 | # Targets to scrape 10 | scrape_configs: 11 | - job_name: 'prometheus' 12 | static_configs: 13 | - targets: ['localhost:9090'] 14 | 15 | - job_name: 'elixir_app' 16 | static_configs: 17 | - targets: ['elixir_app:4000'] 18 | 19 | - job_name: 'postgres_exporter' 20 | static_configs: 21 | - targets: ['postgres_exporter:9187'] 22 | 23 | - job_name: 'rabbitmq' 24 | static_configs: 25 | - targets: ['rabbitmq:15692'] 26 | -------------------------------------------------------------------------------- /docker/rabbitmq/plugins: -------------------------------------------------------------------------------- 1 | [rabbitmq_prometheus,rabbitmq_management]. 2 | -------------------------------------------------------------------------------- /docker/rabbitmq/rabbitmq.conf: -------------------------------------------------------------------------------- 1 | # https://github.com/rabbitmq/rabbitmq-server/blob/master/docs/rabbitmq.conf.example 2 | loopback_users.guest = false 3 | listeners.tcp.default = 5672 4 | management.listener.port = 15672 5 | management.listener.ssl = false 6 | 7 | default_user = rabbitmq 8 | default_pass = rabbitmq 9 | 10 | vm_memory_high_watermark.absolute = 768MiB 11 | vm_memory_high_watermark_paging_ratio = 0.2 12 | 13 | cluster_name = rabbitmq 14 | 15 | # background_gc_enabled = true 16 | 17 | # Increase the 5s default so that we are below Prometheus' scrape interval, 18 | # but still refresh in time for Prometheus scrape 19 | # This is linked to Prometheus scrape interval & range used with rate() 20 | collect_statistics_interval = 10000 21 | 22 | # Run RabbitMQ Management in Management-only mode, no stats 23 | # https://github.com/rabbitmq/rabbitmq-management/pull/707 24 | # management.disable_stats = true 25 | -------------------------------------------------------------------------------- /lib/elixir_popularity.ex: -------------------------------------------------------------------------------- 1 | defmodule ElixirPopularity do 2 | @moduledoc """ 3 | Documentation for ElixirPopularity. 4 | """ 5 | 6 | @doc """ 7 | Hello world. 8 | 9 | ## Examples 10 | 11 | iex> ElixirPopularity.hello() 12 | :world 13 | 14 | """ 15 | def hello do 16 | :world 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/elixir_popularity/application.ex: -------------------------------------------------------------------------------- 1 | defmodule ElixirPopularity.Application do 2 | # See https://hexdocs.pm/elixir/Application.html 3 | # for more information on OTP Applications 4 | @moduledoc false 5 | 6 | use Application 7 | 8 | def start(_type, _args) do 9 | children = [ 10 | :hackney_pool.child_spec(:hn_id_pool, timeout: 15000, max_connections: 100), 11 | ElixirPopularity.Repo, 12 | %{ 13 | id: ElixirPopularity.RMQPublisher, 14 | start: {ElixirPopularity.RMQPublisher, :start_link, []} 15 | }, 16 | %{ 17 | id: ElixirPopularity.HackerNewsIdGenerator, 18 | start: 19 | {ElixirPopularity.HackerNewsIdGenerator, :start_link, 20 | [ 21 | %{ 22 | current_id: 2_306_006, 23 | end_id: 21_672_858, 24 | generate_threshold: 50_000, 25 | batch_size: 30_000, 26 | poll_rate: 30_000 27 | } 28 | ]}, 29 | restart: :transient 30 | }, 31 | ElixirPopularity.HackernewsIdProcessor, 32 | ElixirPopularity.HackernewsPayloadProcessor 33 | ] 34 | 35 | # See https://hexdocs.pm/elixir/Supervisor.html 36 | # for other strategies and supported options 37 | opts = [strategy: :one_for_one, name: ElixirPopularity.Supervisor] 38 | Supervisor.start_link(children, opts) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/elixir_popularity/repo.ex: -------------------------------------------------------------------------------- 1 | defmodule ElixirPopularity.Repo do 2 | use Ecto.Repo, 3 | otp_app: :elixir_popularity, 4 | adapter: Ecto.Adapters.Postgres 5 | end 6 | -------------------------------------------------------------------------------- /lib/hackernews_api.ex: -------------------------------------------------------------------------------- 1 | defmodule ElixirPopularity.HackernewsApi do 2 | @moduledoc """ 3 | Fetches data from the Hackernews API, extracts the necessary fields, 4 | and massages some data 5 | """ 6 | 7 | require Logger 8 | 9 | alias ElixirPopularity.HackernewsItem 10 | 11 | def get_hn_item(resource_id) do 12 | get_hn_item(resource_id, 4) 13 | end 14 | 15 | defp get_hn_item(resource_id, retry) do 16 | response = 17 | resource_id 18 | |> gen_api_url() 19 | |> HTTPoison.get([], hackney: [pool: :hn_id_pool]) 20 | 21 | with {_, {:ok, body}} <- {"hn_api", handle_response(response)}, 22 | {_, {:ok, decoded_payload}} <- {"payload_decode", Jason.decode(body)}, 23 | {_, {:ok, data}} <- {"massage_data", massage_data(decoded_payload)} do 24 | data 25 | else 26 | {stage, error} -> 27 | Logger.warn( 28 | "Failed attempt #{5 - retry} at stage \"#{stage}\" with Hackernews ID of #{resource_id}. Error details: #{ 29 | inspect(error) 30 | }" 31 | ) 32 | 33 | if retry > 0 do 34 | get_hn_item(resource_id, retry - 1) 35 | else 36 | Logger.warn("Failed to retrieve Hackernews ID of #{resource_id}.") 37 | :error 38 | end 39 | end 40 | end 41 | 42 | defp handle_response({:ok, %HTTPoison.Response{status_code: 200, body: body}}) do 43 | {:ok, body} 44 | end 45 | 46 | defp handle_response({_, invalid_response}) do 47 | {:error, invalid_response} 48 | end 49 | 50 | defp gen_api_url(resource_id) do 51 | "https://hacker-news.firebaseio.com/v0/item/#{resource_id}.json" 52 | end 53 | 54 | defp massage_data(data) do 55 | {:ok, 56 | HackernewsItem.create( 57 | data["text"], 58 | data["type"], 59 | data["title"], 60 | data["time"] 61 | )} 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/hackernews_id_processor.ex: -------------------------------------------------------------------------------- 1 | defmodule ElixirPopularity.HackernewsIdProcessor do 2 | use Broadway 3 | 4 | alias Broadway.Message 5 | alias ElixirPopularity.{HackernewsApi, RMQPublisher} 6 | 7 | def start_link(_opts) do 8 | Broadway.start_link(__MODULE__, 9 | name: __MODULE__, 10 | producers: [ 11 | default: [ 12 | module: 13 | {BroadwayRabbitMQ.Producer, 14 | queue: RMQPublisher.item_id_queue_name(), 15 | connection: [ 16 | username: "rabbitmq", 17 | password: "rabbitmq" 18 | ]}, 19 | stages: 1 20 | ] 21 | ], 22 | processors: [ 23 | default: [ 24 | stages: 100 25 | ] 26 | ], 27 | batchers: [ 28 | default: [ 29 | batch_size: 10, 30 | batch_timeout: 10_000, 31 | stages: 2 32 | ] 33 | ] 34 | ) 35 | end 36 | 37 | @impl true 38 | def handle_message(_processor, message, _context) do 39 | Message.update_data(message, fn hn_id -> 40 | {hn_id, HackernewsApi.get_hn_item(hn_id)} 41 | end) 42 | end 43 | 44 | @impl true 45 | def handle_batch(_batcher, messages, _batch_info, _context) do 46 | encoded_payload = 47 | messages 48 | |> Enum.reject(fn 49 | %Message{data: {_id, :error}} -> true 50 | _ -> false 51 | end) 52 | |> Enum.map(fn %Message{data: {id, item}} -> 53 | %{ 54 | id: id, 55 | item: Map.from_struct(item) 56 | } 57 | end) 58 | |> Jason.encode!() 59 | 60 | RMQPublisher.publish_hn_items(encoded_payload) 61 | 62 | messages 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/hackernews_item.ex: -------------------------------------------------------------------------------- 1 | defmodule ElixirPopularity.HackernewsItem do 2 | alias __MODULE__ 3 | 4 | defstruct text: nil, type: nil, title: nil, time: nil 5 | 6 | def create(text, type, title, unix_time) do 7 | %HackernewsItem{ 8 | text: text, 9 | type: type, 10 | title: title, 11 | time: get_date_time(unix_time) 12 | } 13 | end 14 | 15 | defp get_date_time(nil), do: nil 16 | 17 | defp get_date_time(unix_time) do 18 | {:ok, date_time} = DateTime.from_unix(unix_time) 19 | 20 | DateTime.to_date(date_time) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/hackernews_payload_processor.ex: -------------------------------------------------------------------------------- 1 | defmodule ElixirPopularity.HackernewsPayloadProcessor do 2 | use Broadway 3 | 4 | alias Broadway.Message 5 | alias ElixirPopularity.{HNStats, Repo} 6 | 7 | def start_link(_opts) do 8 | Broadway.start_link(__MODULE__, 9 | name: __MODULE__, 10 | producers: [ 11 | default: [ 12 | module: 13 | {BroadwayRabbitMQ.Producer, 14 | queue: ElixirPopularity.RMQPublisher.bulk_item_data_queue_name(), 15 | connection: [ 16 | username: "rabbitmq", 17 | password: "rabbitmq" 18 | ]}, 19 | stages: 1 20 | ] 21 | ], 22 | processors: [ 23 | default: [ 24 | stages: 20 25 | ] 26 | ] 27 | ) 28 | end 29 | 30 | def handle_message(_processor, %Message{data: data} = message, _context) do 31 | data 32 | |> Jason.decode!() 33 | |> Enum.map(fn entry -> 34 | summarize_data(entry) 35 | end) 36 | |> Enum.filter(fn summary -> 37 | summary.language_present 38 | |> Map.values() 39 | |> Enum.member?(true) 40 | end) 41 | |> Enum.each(fn entry -> 42 | date = entry.date 43 | hn_id = entry.id 44 | type = entry.type 45 | 46 | Enum.each(entry.language_present, fn 47 | {lang, true} -> 48 | %HNStats{} 49 | |> HNStats.changeset(%{ 50 | item_id: hn_id, 51 | item_type: type, 52 | language: Atom.to_string(lang), 53 | date: date, 54 | occurances: 1 55 | }) 56 | |> Repo.insert() 57 | 58 | {_lang, false} -> 59 | nil 60 | end) 61 | end) 62 | 63 | message 64 | end 65 | 66 | defp summarize_data(hn_item) do 67 | %{ 68 | id: hn_item["id"], 69 | date: hn_item["item"]["time"], 70 | type: hn_item["item"]["type"], 71 | language_present: language_check(hn_item["item"]) 72 | } 73 | end 74 | 75 | defp language_check(%{"type" => "story", "text" => text}) when not is_nil(text) do 76 | process_text(text) 77 | end 78 | 79 | defp language_check(%{"type" => "story", "title" => text}) when not is_nil(text) do 80 | process_text(text) 81 | end 82 | 83 | defp language_check(%{"type" => "comment", "text" => text}) when not is_nil(text) do 84 | process_text(text) 85 | end 86 | 87 | defp language_check(%{"type" => "job", "text" => text}) when not is_nil(text) do 88 | process_text(text) 89 | end 90 | 91 | defp process_text(text) do 92 | [elixir: ~r/elixir/i, erlang: ~r/erlang/i] 93 | |> Enum.map(fn {lang, regex} -> 94 | {lang, String.match?(text, regex)} 95 | end) 96 | |> Map.new() 97 | end 98 | 99 | defp language_check(_item) do 100 | %{ 101 | elixir: 0, 102 | erlang: 0 103 | } 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /lib/hn_id_generator.ex: -------------------------------------------------------------------------------- 1 | defmodule ElixirPopularity.HackerNewsIdGenerator do 2 | use GenServer 3 | 4 | require Logger 5 | 6 | alias ElixirPopularity.RMQPublisher 7 | 8 | # ---------------- Initialization Functions ---------------- 9 | def start_link(state) do 10 | GenServer.start_link(__MODULE__, state, name: __MODULE__) 11 | end 12 | 13 | @impl true 14 | def init(options) do 15 | state = Map.put(options, :timer_ref, nil) 16 | 17 | {:ok, state} 18 | end 19 | 20 | # ---------------- Public Functions ---------------- 21 | def start_generating, do: GenServer.call(__MODULE__, :start_generating) 22 | 23 | def stop_generating, do: GenServer.call(__MODULE__, :stop_generating) 24 | 25 | # ---------------- Callback Functions ---------------- 26 | @impl true 27 | def handle_info(:poll_queue_size, %{current_id: current_id, end_id: end_id} = state) when current_id > end_id do 28 | # All done with the configured work, nothing else to do 29 | Logger.info("No more HackerNews IDs to generate. My work is done. I can rest now.") 30 | 31 | {:stop, :normal, state} 32 | end 33 | 34 | def handle_info(:poll_queue_size, state) do 35 | # Get the size of the queue using the GenRMQ module 36 | queue_size = RMQPublisher.hn_id_queue_size() 37 | 38 | # Determine if more HackerNews items IDs need to be put on the queue 39 | new_current_id = 40 | if queue_size < state.generate_threshold do 41 | upper_range = min(state.current_id + state.batch_size, state.end_id) 42 | 43 | Logger.info("Enqueuing HackerNews items #{state.current_id} - #{upper_range}") 44 | 45 | # Publish all the new HackerNews items IDs 46 | state.current_id..upper_range 47 | |> Enum.each(fn hn_id -> 48 | RMQPublisher.publish_hn_id("#{hn_id}") 49 | end) 50 | 51 | upper_range + 1 52 | else 53 | Logger.info("Queue size of #{queue_size} is greater than the threshold of #{state.generate_threshold}") 54 | 55 | state.current_id 56 | end 57 | 58 | new_state = 59 | state 60 | |> Map.put(:current_id, new_current_id) 61 | |> Map.put(:timer_ref, schedule_next_poll(state.poll_rate)) 62 | 63 | {:noreply, new_state} 64 | end 65 | 66 | @impl true 67 | def handle_call(:start_generating, _from, state) do 68 | send(self(), :poll_queue_size) 69 | 70 | {:reply, :ok, state} 71 | end 72 | 73 | @impl true 74 | def handle_call(:stop_generating, _from, state) do 75 | Process.cancel_timer(state.timer_ref) 76 | new_state = %{state | timer_ref: nil} 77 | 78 | {:reply, :ok, new_state} 79 | end 80 | 81 | defp schedule_next_poll(poll_rate) do 82 | Logger.info("Scheduling next queue poll") 83 | 84 | Process.send_after(self(), :poll_queue_size, poll_rate) 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /lib/hn_stats.ex: -------------------------------------------------------------------------------- 1 | defmodule ElixirPopularity.HNStats do 2 | use Ecto.Schema 3 | import Ecto.Changeset 4 | 5 | schema "hn_stats" do 6 | field(:item_id, :string) 7 | field(:item_type, :string) 8 | field(:language, :string) 9 | field(:date, :date) 10 | field(:occurances, :integer) 11 | end 12 | 13 | def changeset(hn_stats, params \\ %{}) do 14 | all_fields = ~w(item_id item_type language date occurances)a 15 | 16 | hn_stats 17 | |> cast(params, all_fields) 18 | |> validate_required(all_fields) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/rmq_publisher.ex: -------------------------------------------------------------------------------- 1 | defmodule ElixirPopularity.RMQPublisher do 2 | @behaviour GenRMQ.Publisher 3 | 4 | @rmq_uri "amqp://rabbitmq:rabbitmq@localhost:5672" 5 | @hn_exchange "hn_analytics" 6 | @hn_item_ids_queue "item_ids" 7 | @hn_bulk_item_data_queue "bulk_item_data" 8 | @publish_options [persistent: false] 9 | 10 | def init do 11 | create_rmq_resources() 12 | 13 | [ 14 | uri: @rmq_uri, 15 | exchange: @hn_exchange 16 | ] 17 | end 18 | 19 | def start_link do 20 | GenRMQ.Publisher.start_link(__MODULE__, name: __MODULE__) 21 | end 22 | 23 | def hn_id_queue_size do 24 | GenRMQ.Publisher.message_count(__MODULE__, @hn_item_ids_queue) 25 | end 26 | 27 | def publish_hn_id(hn_id) do 28 | GenRMQ.Publisher.publish(__MODULE__, hn_id, @hn_item_ids_queue, @publish_options) 29 | end 30 | 31 | def publish_hn_items(items) do 32 | GenRMQ.Publisher.publish(__MODULE__, items, @hn_bulk_item_data_queue, @publish_options) 33 | end 34 | 35 | def item_id_queue_name, do: @hn_item_ids_queue 36 | 37 | def bulk_item_data_queue_name, do: @hn_bulk_item_data_queue 38 | 39 | defp create_rmq_resources do 40 | # Setup RabbitMQ connection 41 | {:ok, connection} = AMQP.Connection.open(@rmq_uri) 42 | {:ok, channel} = AMQP.Channel.open(connection) 43 | 44 | # Create exchange 45 | AMQP.Exchange.declare(channel, @hn_exchange, :topic, durable: true) 46 | 47 | # Create queues 48 | AMQP.Queue.declare(channel, @hn_item_ids_queue, durable: true) 49 | AMQP.Queue.declare(channel, @hn_bulk_item_data_queue, durable: true) 50 | 51 | # Bind queues to exchange 52 | AMQP.Queue.bind(channel, @hn_item_ids_queue, @hn_exchange, routing_key: @hn_item_ids_queue) 53 | AMQP.Queue.bind(channel, @hn_bulk_item_data_queue, @hn_exchange, routing_key: @hn_bulk_item_data_queue) 54 | 55 | # Close the channel as it is no longer needed 56 | # GenRMQ will manage its own channel 57 | AMQP.Channel.close(channel) 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule ElixirPopularity.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :elixir_popularity, 7 | version: "0.1.0", 8 | elixir: "~> 1.9", 9 | start_permanent: Mix.env() == :prod, 10 | deps: deps() 11 | ] 12 | end 13 | 14 | # Run "mix help compile.app" to learn about applications. 15 | def application do 16 | [ 17 | extra_applications: [:logger], 18 | mod: {ElixirPopularity.Application, []} 19 | ] 20 | end 21 | 22 | # Run "mix help deps" to learn about dependencies. 23 | defp deps do 24 | [ 25 | {:jason, "~> 1.1.2"}, 26 | {:httpoison, "~> 1.6.1"}, 27 | {:broadway, "~> 0.4.0"}, 28 | {:broadway_rabbitmq, "~> 0.4.0"}, 29 | {:ecto_sql, "~> 3.0"}, 30 | {:postgrex, ">= 0.0.0"}, 31 | {:gen_rmq, "~> 2.3.0"} 32 | ] 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "amqp": {:hex, :amqp, "1.1.0", "86bbea4f4d21b2941d98af1260bdd5ce94978678eb96059eb3065d506970cc16", [:mix], [{:amqp_client, "~> 3.7.9", [hex: :amqp_client, repo: "hexpm", optional: false]}, {:goldrush, "~> 0.1.0", [hex: :goldrush, repo: "hexpm", optional: false]}, {:jsx, "~> 2.9", [hex: :jsx, repo: "hexpm", optional: false]}, {:lager, "~> 3.6.5", [hex: :lager, repo: "hexpm", optional: false]}, {:rabbit_common, "~> 3.7.9", [hex: :rabbit_common, repo: "hexpm", optional: false]}, {:ranch, "~> 1.6.2", [hex: :ranch, repo: "hexpm", optional: false]}, {:ranch_proxy_protocol, "~> 2.1.1", [hex: :ranch_proxy_protocol, repo: "hexpm", optional: false]}, {:recon, "~> 2.3.6", [hex: :recon, repo: "hexpm", optional: false]}], "hexpm"}, 3 | "amqp_client": {:hex, :amqp_client, "3.7.9", "ebef998500c31d4954e882868806258a9cebf01dd6c76804b1a2efdb454753fc", [:make, :rebar3], [{:rabbit_common, "3.7.9", [hex: :rabbit_common, repo: "hexpm", optional: false]}], "hexpm"}, 4 | "broadway": {:hex, :broadway, "0.4.0", "b8daf580baed44347a9690449f9d8d7a308c1ca086648397d1a88eea893d047e", [:mix], [{:gen_stage, "~> 0.14", [hex: :gen_stage, repo: "hexpm", optional: false]}], "hexpm"}, 5 | "broadway_rabbitmq": {:hex, :broadway_rabbitmq, "0.4.0", "5e43ab4d7a0c95521db5368f4b4f7776b732bd0558fb0c55fe424e51d48aafb3", [:mix], [{:amqp, "~> 1.1", [hex: :amqp, repo: "hexpm", optional: false]}, {:broadway, "~> 0.4.0", [hex: :broadway, repo: "hexpm", optional: false]}], "hexpm"}, 6 | "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, 7 | "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"}, 8 | "db_connection": {:hex, :db_connection, "2.1.1", "a51e8a2ee54ef2ae6ec41a668c85787ed40cb8944928c191280fe34c15b76ae5", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"}, 9 | "decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"}, 10 | "ecto": {:hex, :ecto, "3.2.5", "76c864b77948a479e18e69cc1d0f0f4ee7cced1148ffe6a093ff91eba644f0b5", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"}, 11 | "ecto_sql": {:hex, :ecto_sql, "3.2.1", "4eed4100cbb2abcff10c46660d6613693807bf64f1b865f414daccf762d3758d", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.2.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.2.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"}, 12 | "gen_rmq": {:hex, :gen_rmq, "2.3.0", "f7dfaf355f0edeae29b0b97af216d9c02c9469c3f868370e29640afb631d13a7", [:mix], [{:amqp, "~> 1.1", [hex: :amqp, repo: "hexpm", optional: false]}], "hexpm"}, 13 | "gen_stage": {:hex, :gen_stage, "0.14.3", "d0c66f1c87faa301c1a85a809a3ee9097a4264b2edf7644bf5c123237ef732bf", [:mix], [], "hexpm"}, 14 | "goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], [], "hexpm"}, 15 | "hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, 16 | "httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, 17 | "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, 18 | "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, 19 | "jsx": {:hex, :jsx, "2.9.0", "d2f6e5f069c00266cad52fb15d87c428579ea4d7d73a33669e12679e203329dd", [:mix, :rebar3], [], "hexpm"}, 20 | "lager": {:hex, :lager, "3.6.5", "831910109f3fcb503debf658ca0538836b348c58bfbf349a6d48228096ce9040", [:rebar3], [{:goldrush, "0.1.9", [hex: :goldrush, repo: "hexpm", optional: false]}], "hexpm"}, 21 | "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, 22 | "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"}, 23 | "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"}, 24 | "postgrex": {:hex, :postgrex, "0.15.1", "23ce3417de70f4c0e9e7419ad85bdabcc6860a6925fe2c6f3b1b5b1e8e47bf2f", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"}, 25 | "rabbit_common": {:hex, :rabbit_common, "3.7.9", "542d2eb8ef8f76e5346b30665604d8d63b8d5cf47d1878243cec44d1642c4435", [:make, :rebar3], [{:jsx, "2.9.0", [hex: :jsx, repo: "hexpm", optional: false]}, {:lager, "3.6.5", [hex: :lager, repo: "hexpm", optional: false]}, {:ranch, "1.6.2", [hex: :ranch, repo: "hexpm", optional: false]}, {:ranch_proxy_protocol, "2.1.1", [hex: :ranch_proxy_protocol, repo: "hexpm", optional: false]}, {:recon, "2.3.6", [hex: :recon, repo: "hexpm", optional: false]}], "hexpm"}, 26 | "ranch": {:hex, :ranch, "1.6.2", "6db93c78f411ee033dbb18ba8234c5574883acb9a75af0fb90a9b82ea46afa00", [:rebar3], [], "hexpm"}, 27 | "ranch_proxy_protocol": {:hex, :ranch_proxy_protocol, "2.1.1", "3c4723327166d2d63c0405f4914e2e471c6de362cc844e9b203af5763e7c9d25", [:rebar3], [{:ranch, "1.6.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, 28 | "recon": {:hex, :recon, "2.3.6", "2bcad0cf621fb277cabbb6413159cd3aa30265c2dee42c968697988b30108604", [:rebar3], [], "hexpm"}, 29 | "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm"}, 30 | "telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"}, 31 | "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"}, 32 | } 33 | -------------------------------------------------------------------------------- /priv/repo/migrations/20191205052229_create_stats_table.exs: -------------------------------------------------------------------------------- 1 | defmodule ElixirPopularity.Repo.Migrations.CreateStatsTable do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:hn_stats) do 6 | add(:item_id, :string) 7 | add(:item_type, :string) 8 | add(:language, :string) 9 | add(:date, :date) 10 | add(:occurances, :integer) 11 | end 12 | 13 | create(index(:hn_stats, [:date])) 14 | create(index(:hn_stats, [:language])) 15 | create(index(:hn_stats, [:item_id])) 16 | create(index(:hn_stats, [:item_type])) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/elixir_popularity_test.exs: -------------------------------------------------------------------------------- 1 | defmodule ElixirPopularityTest do 2 | use ExUnit.Case 3 | doctest ElixirPopularity 4 | 5 | test "greets the world" do 6 | assert ElixirPopularity.hello() == :world 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | --------------------------------------------------------------------------------