awa_2.4.0_59135a52/awa/plugins/awa-counters/src/awa-counters-modules.ads

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
-----------------------------------------------------------------------
--  awa-counters-modules -- Module counters
--  Copyright (C) 2015, 2018, 2020 Stephane Carrez
--  Written by Stephane Carrez (Stephane.Carrez@gmail.com)
--
--  Licensed under the Apache License, Version 2.0 (the "License");
--  you may not use this file except in compliance with the License.
--  You may obtain a copy of the License at
--
--      http://www.apache.org/licenses/LICENSE-2.0
--
--  Unless required by applicable law or agreed to in writing, software
--  distributed under the License is distributed on an "AS IS" BASIS,
--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--  See the License for the specific language governing permissions and
--  limitations under the License.
-----------------------------------------------------------------------
with Ada.Containers.Indefinite_Hashed_Maps;
with Ada.Calendar;

with ASF.Applications;
with ADO.Sessions;
with AWA.Modules;

--  == Integration ==
--  The `Counter_Module` manages the counters associated with database entities.
--  To avoid having to update the database each time a counter is incremented,
--  counters are kept temporarily in a `Counter_Table` protected type.
--  The table contains only the partial increments and not the real counter
--  values.  Counters are flushed when the table reaches some limit, or,
--  when the table is oldest than some limit.  Counters are associated with
--  a day so that it becomes possible to gather per-day counters.
--  The table is also flushed when a counter is incremented in a different day.
--
--  To be able to use the `Counters` module, you will need to add the
--  following line in your GNAT project file:
--
--    with "awa_counters";
--
--  An instance of the `Counter_Module` must be declared and registered in the
--  AWA application.  The module instance can be defined as follows:
--
--    with AWA.Counters.Modules;
--    ...
--    type Application is new AWA.Applications.Application with record
--       Counter_Module : aliased AWA.Counters.Modules.Counter_Module;
--    end record;
--
--  And registered in the `Initialize_Modules` procedure by using:
--
--    Register (App    => App.Self.all'Access,
--              Name   => AWA.Counters.Modules.NAME,
--              Module => App.Counter_Module'Access);
--
--  == Configuration ==
--  The `counters` module defines the following configuration parameters:
--
--  @include-config counters.xml
--
package AWA.Counters.Modules is

   --  The name under which the module is registered.
   NAME : constant String := "counters";

   --  Default age limit to flush counters: 5 minutes.
   DEFAULT_AGE_LIMIT : constant Duration := 5 * 60.0;

   --  Default maximum number of different counters to keep before flushing.
   DEFAULT_COUNTER_LIMIT : constant Natural := 1_000;

   PARAM_AGE_LIMIT       : constant String := "counter_age_limit";

   PARAM_COUNTER_LIMIT   : constant String := "counter_limit";

   --  ------------------------------
   --  Module counters
   --  ------------------------------
   type Counter_Module is new AWA.Modules.Module with private;
   type Counter_Module_Access is access all Counter_Module'Class;

   --  Initialize the counters module.
   overriding
   procedure Initialize (Plugin : in out Counter_Module;
                         App    : in AWA.Modules.Application_Access;
                         Props  : in ASF.Applications.Config);

   --  Configures the module after its initialization and after having
   --  read its XML configuration.
   overriding
   procedure Configure (Plugin : in out Counter_Module;
                        Props  : in ASF.Applications.Config);

   --  Get the counters module.
   function Get_Counter_Module return Counter_Module_Access;

   --  Increment the counter identified by `Counter` and associated with the
   --  database object `Object`.
   procedure Increment (Plugin  : in out Counter_Module;
                        Counter : in Counter_Index_Type;
                        Object  : in ADO.Objects.Object_Ref'Class);

   --  Increment the counter identified by `Counter` and associated with the
   --  database object key `Key`.
   procedure Increment (Plugin  : in out Counter_Module;
                        Counter : in Counter_Index_Type;
                        Key     : in ADO.Objects.Object_Key);

   --  Increment the counter identified by `Counter`.
   procedure Increment (Plugin  : in out Counter_Module;
                        Counter : in Counter_Index_Type);

   --  Get the current counter value.
   procedure Get_Counter (Plugin  : in out Counter_Module;
                          Counter : in AWA.Counters.Counter_Index_Type;
                          Object  : in ADO.Objects.Object_Ref'Class;
                          Result  : out Natural);

   --  Flush the existing counters and update all the database records
   --  refered to them.
   procedure Flush (Plugin : in out Counter_Module);

private

   type Definition_Array_Type is array (Counter_Index_Type range <>) of Natural;
   type Definition_Array_Type_Access is access all Definition_Array_Type;

   --  The counter map tracks a counter associated with a database object.
   --  All the database objects refer to the same counter.
   package Counter_Maps is
     new Ada.Containers.Indefinite_Hashed_Maps (Key_Type        => ADO.Objects.Object_Key,
                                                Element_Type    => Positive,
                                                Hash            => ADO.Objects.Hash,
                                                Equivalent_Keys => ADO.Objects."=");

   --  The `Counter_Map_Array` associate a counter map to each counter definition.
   type Counter_Map_Array is array (Counter_Index_Type range <>) of Counter_Maps.Map;
   type Counter_Map_Array_Access is access all Counter_Map_Array;

   --  Counters are kept temporarily in the `Counter_Table` protected type to avoid
   --  having to update the database each time a counter is incremented.
   --  Counters are flushed when the table reaches some limit, or, when the
   --  table is oldest than some limit.  Counters are associated with a day
   --  so that it becomes possible to gather per-day counters.
   --
   --  The `Flush` operation on the `Counter_Module` can be used to flush
   --  the pending counters.  For each counter that was updated, it either
   --  inserts or updates a row in the counters database table.  Because such
   --  operation is slow, it is not implemented in the protected type.
   --  Instead, we steal the counter table and replace it with a new/empty
   --  table.  This is done by `Steal_Counters` protected operation.
   --  While doing the flush, other tasks can increment counters without
   --  being blocked by the `Flush` operation.
   protected type Counter_Table is

      --  Increment the counter identified by `Counter` and associated with the
      --  database object <tt>Key</tt>.
      procedure Increment (Counter : in Counter_Index_Type;
                           Key     : in ADO.Objects.Object_Key);

      --  Get the counters that have been collected with the date and
      --  prepare to collect new counters.
      procedure Steal_Counters (Result : out Counter_Map_Array_Access;
                                Date   : out Ada.Calendar.Time);

      --  Check if we must flush the counters.
      function Need_Flush (Limit   : in Natural;
                           Seconds : in Duration) return Boolean;

      --  Get the definition ID associated with the counter.
      procedure Get_Definition (Session : in out ADO.Sessions.Master_Session;
                                Counter : in Counter_Index_Type;
                                Result  : out Natural);

   private
      Day         : Ada.Calendar.Time;
      Day_End     : Ada.Calendar.Time;
      Counters    : Counter_Map_Array_Access;
      Definitions : Definition_Array_Type_Access;
      Nb_Counters : Natural := 0;
   end Counter_Table;

   type Counter_Module is new AWA.Modules.Module with record
      Counters      : Counter_Table;
      Counter_Limit : Natural  := DEFAULT_COUNTER_LIMIT;
      Age_Limit     : Duration := DEFAULT_AGE_LIMIT;
   end record;

end AWA.Counters.Modules;