[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

TimeFilter text (summary for review)



Hi,

We need to finish up the TimeFilter discussions so RMON-2 can
be republished, so that documents waiting for RFC 2021-bis can
move along.

Attached is the proposed TimeFilter TC (para 1) and complete
appendix section.  Please send any comments to the list ASAP,
otherwise we will use this text in the next draft.

thanks,
Andy
TimeFilter TC: para 1:

OLD:
        "To be used for the index to a table.  Allows an application
        to download only those rows changed since a particular time.
        A row is considered changed if the value of any object in the
        row changes or if the row is created or deleted.

NEW:
        "To be used for the index to a table.  Allows an application
        to download only those rows changed since a particular time.
        A row is considered changed if the value of any object in the
        row changes, if the row is created, or if any object in the 
        row is created or deleted.  Note that deleted rows cannot be 
        detected or downloaded."

--------------------------------------------------------------------

8.  Appendix - TimeFilter Implementation Notes

1) Theory of Operation

The TimeFilter mechanism allows an NMS to reduce the number of
SNMP transactions required for a 'table-update' operation,
by retrieving only the rows that have changed since a specified
time (usually the last poll time).  Polling of tables that 
incorporate a 'TimeFilter' INDEX can be reduced to a theoretical 
minimum (if used correctly). It can be easily implemented by an 
agent in a way independent of the number of NMS applications using 
the same time-filtered table.

Although the name 'TimeFilter' may imply that a history of
change events is maintained by the agent, this is not the
case.  A time-filtered-value represents the current value of
the object instance, not the 'saved' value at the time
indicated by the TimeFilter INDEX value.  Note that TimeFilter
objects only appear in INDEX clauses (always not-accessible),
so their value is never retrieved. By design, the actual value
of a TimeFilter instance is not in itself meaningful (it's not
a 'last-change-timestamp').

The TimeFilter is a boolean filtering function applied in
internal Get* PDU processing. If the 'last-change-time' of the
specified instance is less than the particular TimeFilter
INDEX value, then the instance is considered 'not-present',
and it is skipped for GetNext and GetBulk PDUs, or 
a 'noSuchInstance' exception is returned for Get PDUs.

1.1) Agent Implementation of a Time-Filtered Table

In implementation, the time-filtered rows (one for each tick
of sysUpTime) are only conceptual. The agent simply filters a
real table based on:
    * the current value of sysUpTime
    * the TimeFilter value passed in the varbind
    * the last-update timestamp of each requested row
      (agent implementation requirement)

For example, to implement a time-filtered table row
(e.g., set of counters), an agent maintains a timestamp 
in a 32-bit storage location, initialized to zero.  This 
is in addition to whatever instrumentation is needed for 
the set of counters.

Each time one of the counters is updated, the current value of
sysUpTime is recorded in the associated timestamp. If this is
not possible or practical, then a background polling process
must 'refresh' the timestamp by sampling counter values and
comparing them to recorded samples. The timestamp update must
occur within 5 seconds of the actual change event.

When an agent receives a Get, GetNext, or GetBulk PDU
requesting a time-filtered instance, after the agent has
determined that the instance is within the specified MIB view,
the following conceptual test is applied to determine if the 
object is returned or filtered:

    /* return TRUE if the object is present */
    boolean time_filter_test (
        TimeFilter  last_modified_timestamp,
        TimeFilter  index_value_in_pdu )
    {
        if (last_modified_timestamp < index_value_in_pdu)
            return FALSE;
        else
            return TRUE;
    }

The agent applies this function regardless of the
lastActivationTime of the conceptual row in question. In other
words, counter discontinuities are ignored (i.e.  conceptual
row deleted and then re-created later). An agent should
consider a object instance 'changed' when it is created
(either at restart time for scalars and static objects, or
row-creation-time for dynamic tables).

Note that using a timeFilter INDEX value of zero removes the
filtering functionality, as the instance will always be
'present' according to the test above.

After some deployment experience, it has been determined that a
time-filtered table is more efficient to use if the agent
stops a "MIB walk" operation after one time-filtered entry.  
That is, a GetNext or GetBulk operation will provide one pass 
through a given table, i.e., the agent will continue to the 
next object or table, instead of incrementing a TimeMark INDEX 
value, even if there exists higher TimeMark values which are 
valid for the same conceptual row.

It is acceptable for an agent to implement a time-filtered table
in this manner, or in the traditional manner (i.e., every
conceptual time-filtered instance is returned in GetNext
and GetBulk PDU responses).

1.2) NMS Implementation of a Time-Filtered Table

The particular TimeFilter INDEX values used by an NMS reflect
the polling interval of the NMS, relative to the particular
agent's notion of sysUpTime.

An NMS needs to maintain one timestamp variable per agent
(initialized to zero) for an arbitrary group of time-filtered
MIB objects that are gathered together in the same PDU.  Each
time the Get* PDU is sent, a request for sysUpTime is
included. The retrieved sysUpTime value is used as the
timeFilter value in the next polling cycle. If a polling sweep
of a time-filtered group of objects requires more than one
SNMP transaction, then the sysUpTime value retrieved in the
first GetResponse PDU of the polling sweep is saved as the
next timeFilter value.

The actual last-update time of a given object is not indicated
in the returned GetResponse instance identifier, but rather
the timeFilter value passed in the Get*Request PDU is
returned.

A "time-filtered get-next/bulk-sweep", done once per polling
cycle, is a series of GetNext or GetBulk transactions, and is
over when one of the following events occurs:
  1) the TimeFilter index value returned in the GetResponse is
     different than the TimeFilter index value passed in the
     GetNext or GetBulk request. Counter values will still be
     returned beyond this point (until the last-change-time is
     reached), but most likely the same values will be
     returned.
  2) the return PDU includes instances lexigraphically greater
     than the objects expected (i.e. same GetNext semantics as
     if the TimeFilter wasn't there)
  3) a noSuchName or other exception/error is returned.

Note that the use of a time-filtered table in combination with
a GetRequest PDU neutralizes any optimization that otherwise
might be achieved with the TimeFilter, because no PDU
transactions are saved. Either the current time-filtered
object-value is returned, or a 'noSuchInstance' exception
(SNMPv1c) or 'noSuchName' error (SNMPv1) is returned.

If GetBulk PDUs are used, then the value selected for response
PDUs generated by the agent, since duplicate entries (one per
'tick' of sysUpTime) are likely to pad the PDU to its maximum
size. An appropriate of conceptual rows in the time-filtered
table if known, or equal to the number of instances expected
to fit in a GetResponse PDU without causing a 'tooBig' error
from the agent.

2) TimeFilter Example

The following example demonstrates how an NMS and Agent might
use a table with a TimeFilter object in the INDEX. A static
table is assumed to keep the example simple, but dynamic
tables can also be supported.

2.1) General Assumptions

   fooEntry INDEX { fooTimeMark, fooIfIndex }
   FooEntry = SEQUENCE {
       fooTimeMark    TimeFilter,
       fooIfIndex     Integer32,
       fooCounts      Counter32
   }

   The NMS polls the fooTable every 15 seconds and the
   baseline poll occurs when the agent has been up for
   6 seconds, and the NMS has been up for 10 seconds.

   There are 2 static rows in this table at system
   initialization (fooCounts.0.1 and fooCounts.0.2).

   Row 1 was updated as follows:
       SysUpTime    fooCounts.*.1 value
          500            1
          900            2
          2300           3

   Row 2 was updated as follows:
       SysUpTime    fooCounts.*.2 value
          1100           1
          1400           2

2.2) SNMP Transactions from NMS Perspective

   Time nms-1000:
       # NMS baseline poll -- get everything since last agent
       # restart - TimeFilter == 0

       get-bulk(nonRptrs=1, maxReps=2, sysUpTime.0,
            fooCounts.0);
       returns:
          sysUpTime.0 == 600
          fooCounts.0.1 == 1  # incremented at time 500
          fooCounts.0.2 == 0  # visible; created at time 0

   Time nms-2500:
       # NMS 1st poll
       # TimeFilter index == 600

       get-bulk(nonRptrs=1, maxReps=2, sysUpTime.0,
            fooCounts.600);
       returns:
          sysUpTime.0 == 2100
          fooCounts.600.1 == 2   # incremented at time 900
          fooCounts.600.2 == 2   # incremented at times
                                 # 1100 and 1400
          fooCounts.601.1 == 2   # indicates end of sweep

   Time nms-4000:
       # NMS 2nd poll
       # TimeFilter == 2100

       get-bulk(nonRptrs=1, maxReps=2, sysUpTime.0,
            fooCounts.2100);
       returns:
           sysUpTime.0 == 3600
           fooCounts.2100.1 == 3  # incremented at time 2300
           fooCounts.2102.1 == 3  # indicates end-of-sweep

       # the counter value for row 2 is not returned because
       # it hasn't changed since sysUpTime == 2100.
       # The next timetick value for row 1 is returned instead

   Time nms-5500:
       # NMS 3rd poll
       # TimeFilter == 3600

       get-bulk(nonRptrs=1, maxReps=2, sysUpTime.0,
             fooCounts.3600);
       returns:
           sysUpTime.0 == 5100
           some-instance-outside-the-fooTable == <don't care>
           some-instance-outside-the-fooTable == <don't care>

       # no 'fooTable' counter values at all are returned
       # because neither counter has been updated since
       # sysUpTime == 3600


2.3) Transactions and TimeFilter Maintenance: Agent
Perspective

   Time agt-0:
       # initialize fooTable
       fooCounts.1 = 0; changed.1 = 0;
       fooCounts.2 = 0; changed.2 = 0;

   Time agt-500:
       # increment fooCounts.1
       ++fooCounts.1; changed.1 = 500;

   Time agt-600
       # answer get-bulk
       #   get-bulk(nonRptrs=1, maxReps=2, sysUpTime.0,
       #       fooCounts.0);
       # (changed >= 0)
       # return both counters

   Time agt-900:
       # increment fooCounts.1
       ++fooCounts.1; changed.1 = 900;

   Time agt-1100:
       # increment fooCounts.2
       ++fooCounts.2; changed.2 = 1100;

   Time agt-1400:
       # increment fooCounts.2
       ++fooCounts.2; changed.2 = 1400;

   Time agt-2100
       # answer get-bulk
       # get-bulk(nonRptrs=1, maxReps=2, sysUpTime.0,
       #     fooCounts.600);
       # (changed >= 600)
       # return both counters

   Time agt-2300:
       # increment fooCounts.1
       ++fooCounts.1; changed.1 = 2300;

   Time agt-3600:
       # answer get-bulk
       # get-bulk(nonRptrs=1, maxReps=2, sysUpTime.0,
       #     fooCounts.2100);
       # (changed >= 2100)
       # return only fooCounts.1 from the fooTable--twice

   Time agt-5100:
       # answer get-bulk
       # get-bulk(nonRptrs=1, maxReps=2, sysUpTime.0,
       #      fooCounts.3600);
       # (changed >= 3600)
       # return lexigraphically-next two MIB instances