comp730:MeAndYou Project Database Notes


 * MeAndYou

Database Requirements Elicitation:

 * 1.	DB must be able to store data.
 * 2.	DB must be able to run queries.
 * 3.	Storage space needed for data: 10 GB max.
 * 4.	Database Management System (DBMS) must have a secure and encrypted connection. (Front-End)
 * 5.	DB must provide access control for end user, moderator and administrator.
 * 6.	DB must have resiliency to keep track of user records.
 * 7.	DB needs to have a back-up (redundancy).
 * 8.	DB must use relational database model and transactional (OLTP).

Questions for Software Development:

 * 1.	 What programming languages are you using? C#, Java
 * 2.	 How they plan on storing/retrieving data.
 * 3.	 What are the constraints for password?
 * 4.	 How is the data stored? How is it formatted?
 * 5.	 Are there large images being used? No. There are no profile images, as there are no profiles.
 * 6.	 What specific data will you need from the database team? How do you want it sorted or grouped.

Questions for Front-End (GUI):

 * 1.	 What database calls do you need to make, and from which pages?
 * 2.	 What types of data are you going to be gathering from the database to compare to forms data that the user inputs?
 * 3.	 How are you going to verify the security descriptors before you authorize access to the database?
 * 4.	 Please explain how you will use the various database statements, in line with your PHP code, such as SELECT, INSERT, UPDATE, DELETE, based on the data model that has been finalized as of 4/6/2017.
 * 5.	 How will you maintain security over the database, by limiting access to specific user credentials?
 * 6.	 How familiar are you with the PHP language, using the database calls?
 * 7.	 What user accounts do you need setup within the Database Management System (DBMS) to allow specific accesses of data, but prevent SQL injection and other types of hacking?

3/15/17

 * Possible languages = MySQL and Microsoft SQL Server


 * Initial Rough Timeline:
 * March 23rd = Touch Base after exam.
 * March 30th = Model (Pre-check)
 * April 6th = Model (Final-check)
 * April 13th = Database creation
 * April 20th = Database creation
 * April 27th = Demo
 * May 4th = Test iterate
 * May 11th = Last class

3/22/17 MidTerm Exam

 * Briefly touched base. Assigned tasks using GANTT chart
 * DB Creation: Patrick and John
 * Attributes: Patrick and Andry
 * Use Cases: Tiffany and Andry
 * ERD Completion: Everyone

3/23/17

 * Patrick resurrected the old versions of the SQL code, and created a new version based on combining the old versions. This was put up for debate.

3/31/17

 * End of the week meeting!
 * Reviewed progress
 * Need to revise data model

4/4/17

 * 4:00-5:00PM - Meeting with Software Development to discuss data model needs.
 * 7:00-8:00PM - Meeting with Front-End to discuss progress towards fixing the GUI functionality.
 * Andry and Patrick worked on several diagrams, i.e. activity diagram, simple use case
 * Patrick started working on the new SQL code, based on the new ERD that everyone agreed to.

4/6/17

 * Patrick converted agreed upon data model into SQL code, including sample data.



-- -- Database: 'meandyou2' -- -- Version:      2017-04-06 v12 -- Developer:   Patrick R. McElhiney -- Organization: MCE123 c/o University of New Hampshire, Manchester DROP DATABASE MEANDYOU2; CREATE DATABASE MEANDYOU2; USE MEANDYOU2; SET time_zone = "-05:00"; CREATE TABLE notifications (   id MEDIUMINT NOT NULL AUTO_INCREMENT,    value VARCHAR(256),    linksTo VARCHAR(256),    PRIMARY KEY (id)  )  ENGINE=InnoDB DEFAULT CHARSET=latin1; CREATE TABLE userNotifications (   id MEDIUMINT NOT NULL AUTO_INCREMENT,    personId MEDIUMINT,    notificationsId MEDIUMINT,    hasBeenViewed INT(1),    thisTimeStamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,    PRIMARY KEY (id)  )  ENGINE=InnoDB DEFAULT CHARSET=latin1; CREATE TABLE person (     id MEDIUMINT AUTO_INCREMENT,      userName VARCHAR(50) NOT NULL UNIQUE,      firstName VARCHAR(30) NOT NULL,      middleName VARCHAR(30),      lastName VARCHAR(30) NOT NULL,      password VARCHAR(30) NOT NULL,      secretAnswer1 VARCHAR(256),      secretAnswer2 VARCHAR(256),      secretAnswer3 VARCHAR(256),      thisTimeStamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,      PRIMARY KEY (id)  )  ENGINE=InnoDB DEFAULT CHARSET=latin1; CREATE TABLE roles (     id MEDIUMINT NOT NULL AUTO_INCREMENT,      personId MEDIUMINT NOT NULL,      roleType INT(1) NOT NULL,      roleName VARCHAR(50),      thisTimeStamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,      PRIMARY KEY (id)  )  ENGINE=InnoDB DEFAULT CHARSET=latin1; CREATE TABLE matches (     id MEDIUMINT NOT NULL AUTO_INCREMENT,      searchPersonId MEDIUMINT NOT NULL,      matchPersonId MEDIUMINT NOT NULL,      matchType VARCHAR(30),      matchPercent MEDIUMINT(3) DEFAULT NULL,      thisTimeStamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,      PRIMARY KEY (id)  )  ENGINE=InnoDB DEFAULT CHARSET=latin1; CREATE TABLE searchAttribute (     id MEDIUMINT NOT NULL AUTO_INCREMENT,      searchesId MEDIUMINT DEFAULT NULL,      personId MEDIUMINT NOT NULL,      attributeId MEDIUMINT NOT NULL,      PRIMARY KEY (id)  )  ENGINE=InnoDB DEFAULT CHARSET=latin1; CREATE TABLE searches (     id MEDIUMINT NOT NULL AUTO_INCREMENT,      personId MEDIUMINT NOT NULL,      nameOfSearch VARCHAR(50),      searchType VARCHAR(50),      thisTimeStamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,      thisstatus VARCHAR(50),      PRIMARY KEY (id)  )  ENGINE=InnoDB DEFAULT CHARSET=latin1; CREATE TABLE attribute (     id MEDIUMINT NOT NULL AUTO_INCREMENT,      searchesId MEDIUMINT,      attributeTypeId MEDIUMINT NOT NULL,      personId MEDIUMINT,      thisTimeStamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,      itemValue VARCHAR(256),      PRIMARY KEY (id)  )  ENGINE=InnoDB DEFAULT CHARSET=latin1; CREATE TABLE attributeType (     id MEDIUMINT NOT NULL AUTO_INCREMENT,      name VARCHAR(50),      friendly_name VARCHAR(50),      data_type VARCHAR(50),      minLength INT(3),      maxLength INT(3),      regex VARCHAR(1024),      PRIMARY KEY (id)  )  ENGINE=InnoDB DEFAULT CHARSET=latin1; -- -- Foreign keys for table 'userNotifications' -- ALTER TABLE userNotifications ADD CONSTRAINT userNotificationsPersonIdfk FOREIGN KEY (personId) REFERENCES person (id), ADD CONSTRAINT userNotificationsNotificationsIdfk FOREIGN KEY (notificationsId) REFERENCES notifications (id); -- -- Foreign keys for table 'roles' -- ALTER TABLE roles ADD CONSTRAINT rolesPersonIdfk FOREIGN KEY (personId) REFERENCES person (id); -- -- Foreign keys for table 'matches' -- ALTER TABLE matches ADD CONSTRAINT matchesSearchPersonIdfk FOREIGN KEY (searchPersonId) REFERENCES person (id), ADD CONSTRAINT matchesMatchPersonIdfk FOREIGN KEY (matchPersonId) REFERENCES person (id); -- -- Foreign keys for table 'searchAttribute' -- ALTER TABLE searchAttribute ADD CONSTRAINT searchAttributePersonIdfk FOREIGN KEY (personId) REFERENCES person (id), ADD CONSTRAINT searchAttributeAttributeIdfk FOREIGN KEY (attributeId) REFERENCES attribute (id), ADD CONSTRAINT searchAttributeSearchesIdfk FOREIGN KEY (searchesId) REFERENCES searches (id); -- -- Foreign keys for table 'searches' -- ALTER TABLE searches ADD CONSTRAINT searchesPersonIdfk FOREIGN KEY (personId) REFERENCES person (id); -- -- Foreign keys for table 'attribute' -- ALTER TABLE attribute ADD CONSTRAINT attributeSearchesIdfk FOREIGN KEY (searchesId) REFERENCES searches (id), ADD CONSTRAINT attributePersonIdfk FOREIGN KEY (personId) REFERENCES person (id), ADD CONSTRAINT attributeAttributeTypeIdfk FOREIGN KEY (attributeTypeId) REFERENCES attributeType (id);

4/7/17

 * E-mailed the MEANDYOU2 database to all team members of all three groups.
 * Setup the MEANDYOU2 database on lamp.unh.edu and notified the team that it had been installed in #database on oose-team.slack.com
 * Noted that there is a limitation with MySQL importing a "0" index on PRIMARY KEY - it just inserts it as if it's "1" and then continues on, instead of paying attention to the numbering of the index. It does not happen in Linux - this has been addressed and fixed by Patrick.

4/8/17

 * Access Control is setup on the server (LAMP) for MySQL.

There are three different sets of user credentials for MySQL now.

*Username: root Password: pw=root0408123 This account is used for phpMyAdmin only, because it has global access!

*Username: meandyou Password: youandme2123 This account has database-specific access to "MEANDYOU" database, excluding GRANT access.

*Username: meandyou2 Password: youandme2123 This account has database-specific access to "MEANDYOU2" database, excluding GRANT access.

4/11/17

 * 4:00-5:00PM - Meeting with Software Development to discuss what else they need from us. (Unconfirmed)
 * 7:00-8:00PM - Meeting with Front-End (GUI) to discuss what else they need from us. (William R., Josh Q.)

4/12/17

 * In-Class Meeting - What Do We Need To Accomplish?
 * 1.	 Make sure that Software Development and Front-End Understand the Data Model, and can setup the new Database MEANDYOU2.
 * 2.	 Figure out if any other the other groups' team members need help with what they are doing, as the Database work is essentially done, now, other than the data fabrication and the documentation. Areas where other teams may need help include: MySQL Database Calls, PHP Coding, HTML/CSS Coding
 * 3.	 Coordinate data fabrication with John Vey - Patrick M. needs all of the data from him, in order to build the Excel Spreadsheets and SQL Tables.
 * 4.	 Coordinate update of Jaden's Sequence Diagrams, and Upload them to the Wiki.
 * 5.	 Coordinate update of Tiffany's ERD and other diagrams
 * 6.	 Quality Assurance Questions are Due By The End of Class
 * 7.	 Attributes List Needs To Be Posted By The End of Class
 * 8.	 Generate PHP Calls for Database for Software and Front-End
 * 9.	 Get SVN Server Populated With Versions of SQL Code, Data Models (ERDs), and Diagrams
 * 10.	 Architects' Meeting, 8:30PM-9:00PM.

4/17/17

 * Dump data into database

4/19/17

 * Start to test Database/Software for QA Issues
 * Coordinate Testing with Software Development
 * Fix Issues with Database and Prepare for Prototype
 * Ensure that all the bugs are worked out

4/26/17

 * Fix issues with incompatibilities between Software & Database, Front-end & Database
 * Wiki step by step screenshots for Demo

5/2/17

 * Package up Files for Next Year’s Class

4/8/16

 * P1 CheckPoint
 * Appear to be in good shape on our end but need to coordinate with other groups

4/13/16

 * Arrived at rough timeline on whiteboard during class time.
 * Need to formalize timeline and detail tasks.

4/20/16

 * Update database with attributes (Mark & Andrew - Friday 4/22)
 * Update attribute docs (Dan - Friday 4/22)
 * Update docs for wiki (Neal & Ryan)
 * Update Wiki (illya)
 * Plan demo for next week --> GUI linked to DB

4/27/16

 * Assignments for the week:
 * * Revision control - Ryan & Neil - Fri 29 update at Google Hangout
 * * Update server with latest rev of DB (final changes) - Andrew & Mark
 * * Update Master Attributes Doc to reflect the DB - Dan
 * * Functional Requirements Wiki - illya


 * Slight change of pace


 * It didn't look like the tester subgroup was really going to be needed.
 * * Rev control in place
 * * DB updated made and looks like we are ready for final testing
 * * Master attributes doc updated and will be uploaded
 * * Wiki is moving along, need to perhaps define the final deliverables.

5/4/16

 * The Database -- Courtesy of Andrew George & Mark Tollick


 * This week Mark and I made some near-final changes to the database code. We decided to add indexes to the tables to better optimize the efficiency of the database queries. Indexes are used to find rows with specific column values quickly. Without an index, MySQL must begin with the first row and then read through the entire table to find the relevant rows. The larger the table, the more this costs. If the table has an index for the columns in question, MySQL can quickly determine the position to seek to in the middle of the data file without having to look at all the data. This is much faster than reading every row sequentially (dev.mysql.com).


 * The ‘matches’ table has a unique index applied to it so that adding duplicate entries will throw an error. This should prevent injecting redundant data into the table so that the engine will not need to check for duplicates. To test this, we added three insert statements into the ‘matches’ table. Two entries are exactly the same and one is the same match, but reversed (scenario would be if both users had searched for each other). After running the test insert statements, the database showed an error when trying to add the duplicate entry.


 * After several incidents of completely losing our database in the Windows server virtual machine and having to deal with the latency of a non-optimized database environment, Mark and I decided that we would take the initiative to spin up an additional virtual machine to dedicate to our database. The operating system we chose to use was CentOS (Community Enterprise Operating System). The CentOS Project is a community-driven free software effort focused around the goal of providing a rich base platform for open source communities to build upon (centos.org/about/).


 * We provisioned two processors, eight GB of RAM, and twenty-five GB of disk storage to the new virtual machine. Once the VM was built, we installed CentOS on it and configured the network settings so that the Windows VM could communicate with it. We gave it an IP address of: 132.177.188.85. To make sure this was a safe IP address to use, we pinged it from the Windows VM and ran ‘arp -a’ to make sure that there was not a device assigned to that IP. The issue we ran into with the public IP address that we gave it, was that it was unable to reach out to the internet. Even though the IP address is a public IP, we assumed there must be some kind of firewall rule(s) preventing that IP from getting out to the Internet.


 * Once the CentOS VM was configured, we ran into another problem when faced with having to run the create.sql code. Since we were accessing the CentOS VM via the vSphere Client console feature, there was no way to copy and paste over the code. Our workaround to this issue was to download PuTTY to the Windows VM and SSH into the CentOS VM. After setting up SSH successfully, we were able to set a password to the database (same as the password to the Windows VM) and paste the create.sql code.


 * Running tests on both VM’s showed that the CentOS database was exponentially faster than the Windows database. Running the entire create.sql file, the results showed that the CentOS VM created the entire database in 6.08 seconds. The Windows VM ran the same code in 64.33 seconds. This proves how much more efficient the CentOS VM is while only using half the resources of the Windows VM. Running MySQL inside a Linux environment is far superior when it comes to speed and efficiency. As far as how the CentOS VM database will integrate with the other team’s code, they should just be able to point their modules to the IP address of the database VM. Unfortunately, we are not sure if the other teams would be willing to integrate their modules with our CentOS database, so we will need to have a meeting with the other teams to discuss options.


 * Our next goal was to upload 20,000 profile entries into the database. To obtain 20,000 unique records, we decided to use a random data generator from the web, called Mockaroo (www.mockaroo.com). This website only let us create 1,000 entries at a time, so we generated 20 separate personXX.sql files with random data for each column in the person table. Unfortunately, Windows clipboard apparently can’t handle copying and pasting more than 10 insert statements at a time. So, to get the files onto the CentOS VM and into the database, we started with uploading the files to the Windows VM via Google Drive. Then we downloaded WinSCP to transfer the files over to the CentOS VM. Since WinSCP uses the SSH protocol, it automatically recognized the settings I had saved in PuTTY and imported them. Once the files were on the CentOS VM, we used a MySQL command to point the database to the insert statement files (mysql> \. file_name). Inserting 20 files of 1,000 entries a piece took only a matter of seconds.


 * The Windows VM however, was a little different. We had to specify the exact path of the destination file and when executed, the one file took over 5 mins to process. Due to the additional amount of time needed to upload the files, we only uploaded 2,000 entries into the Windows VM database. As the insert statements were being processed by the Windows VM, I brought up the task manager of the Windows VM and noticed that resources consumed were lower than I had expected. We assume the latency of the Windows VM is due to how un-optimized the Windows environment is for Linux-based MySQL.


 * Overall, this week was a very productive one. We were able to create a more optimized environment for the database by spinning up a separate virtual machine running CentOS. Produced a database that massively out performs our previous Windows database using only half of the hardware resources provisioned to it. Enabled network availability so that the other team’s modules can communicate with it if they so choose. And met the 20,000 profile entries requirement.


 * At this point, our database is ready for final prototype version testing.

5/11/16

 * The Database -- Courtesy of Andrew George & Mark Tollick


 * This week was about updating the Wiki pages and making sure there were no discrepancies in the database MySQL code. During the last class, the professor assigned us the task of making the software engine communicate with the CentOS virtual machine (VM) database. Tucker and I (Andrew) put our heads together to troubleshoot the problem. Tucker was able to point the engine to the IP address of the CentOS VM. The first problem we encountered was that the java code was not able to connect to the database. As a result, we gave it the proper login credentials to access the database so that it can run its functions. Once we were able to get the engine to connect to the database, we ran into a second problem. After running the java code from the Windows VM, we noticed that it was throwing an error when trying to access the database. This took some research before we discovered that we needed to give the Windows VM specific access privileges to the database. We granted access to the database, but only for access requests coming from the IP address of the Windows server VM. The result was that a small amount of security had been implemented due to the Windows VM being the only one with access privileges to the CentOS VM database.


 * As a final summary of our database code, the following is all the code we have built for the MeAndYou.us database:

USE meandyou;

DROP DATABASE meandyou;

CREATE DATABASE meandyou;

USE meandyou;

/* This is the main profile table */

CREATE TABLE person (   userName VARCHAR(50) NOT NULL PRIMARY KEY,    password VARCHAR(30) NOT NULL,    firstName VARCHAR(30) NOT NULL,    lastName VARCHAR(30) NOT NULL,    phoneNumber VARCHAR(20) NOT NULL,    gender VARCHAR(6) NOT NULL,    birthDate DATE NOT NULL,    address VARCHAR(50) NOT NULL,    city VARCHAR(15) NOT NULL,    st VARCHAR(30) NOT NULL,    zipCode INT(5) NOT NULL,    dateTimeStamp TIMESTAMP  );

/* This is where the matches are populated */

CREATE TABLE matches (   userName1 VARCHAR(30) NOT NULL,    userName2 VARCHAR(30) NOT NULL,    matchCriteria VARCHAR(50) NOT NULL,    matchPercent INT(3),    dateTimeStamp TIMESTAMP,    foreign key (userName1) references person(userName),    foreign key (userName2) references person(userName)  );

/* This is where the active searches are populated and kept track of */

CREATE TABLE searches (   searchID INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,    userName VARCHAR(50) NOT NULL,    userNameSearch VARCHAR(50),    firstName VARCHAR(30),    lastName VARCHAR(30),    status VARCHAR(10),    dateTimeStamp TIMESTAMP,    foreign key (userName) references person(userName)  );

/* This is where multiple email addresses can be stored for a person’s profile */

CREATE TABLE emails (   userName VARCHAR(50) NOT NULL,    email VARCHAR(30) NOT NULL,    dateTimeStamp TIMESTAMP,    foreign key (userName) references person(userName),    primary key (userName, email)  );

/* This is where multiple phone numbers can be stored for a person’s profile */

CREATE TABLE phones (   userName VARCHAR(50) NOT NULL,    phone VARCHAR(20) NOT NULL,    dateTimeStamp TIMESTAMP,    foreign key (userName) references person(userName),    primary key (userName, phone)   );

/* The following indexes were put in place to better optimize the efficiency and speed of the database when being queried */

CREATE INDEX first_name ON person(firstName);

CREATE INDEX last_name ON person(lastName);

CREATE INDEX user_name ON searches(userName);

CREATE INDEX email ON emails(email);

CREATE INDEX phone_number ON phones(phone);

/* This unique index prevents duplicate entries in the matches table */

CREATE UNIQUE INDEX username_match ON matches (userName1, userName2);

/* This view statement creates a table that updates in real-time when changes are made to the database to better assist the other modules in grabbing important data needed to reference matches and other profile information */

CREATE VIEW profileView AS SELECT person.userName, person.firstName, person.lastName, person.gender, person.birthDate, person.address, person.city, person.st, person.zipCode, emails.email, phones.phone FROM person INNER JOIN emails ON person.userName = emails.userName INNER JOIN phones ON person.userName = phones.userName;

/* This section points the database to the following files to insert 20,000 profiles into the person table */

source person01.sql source person02.sql source person03.sql source person04.sql source person05.sql source person06.sql source person07.sql source person08.sql source person09.sql source person10.sql source person11.sql source person12.sql source person13.sql source person14.sql source person15.sql source person16.sql source person17.sql source person18.sql source person19.sql source person20.sql

/* The following insert statements are to test the CREATE VIEW statement from above */

insert into emails (userName, email) values ('egreene0@topsy.com', 'bscott0@geocities.jp'); insert into emails (userName, email) values ('emontgomery1@army.mil', 'kburns1@issuu.com'); insert into emails (userName, email) values ('arivera2@aboutads.info', 'jjenkins2@bloglovin.com'); insert into emails (userName, email) values ('dhawkins3@livejournal.com', 'swebb3@smugmug.com');

insert into phones (userName, phone) values ('egreene0@topsy.com', '62-(348)299-5105'); insert into phones (userName, phone) values ('emontgomery1@army.mil', '351-(150)235-0118'); insert into phones (userName, phone) values ('arivera2@aboutads.info', '976-(282)201-6261'); insert into phones (userName, phone) values ('dhawkins3@livejournal.com', '86-(268)277-0033');

/* This select statement displays the CREATE VIEW table */

select * from profileView;

 The Master Attribute List -- Courtesy of Daniel Stoller 

The following are the attributes for each table. The ones labeled (pk) are primary keys; the ones labeled (fk) are foreign keys.
 * Person table: has no foreign keys
 * username (pk) [varchar]   -  is the email
 * password [varchar]
 * firstName [varchar]
 * lastName [varchar]
 * phoneNumber [varchar]
 * gender [varchar]
 * birthDate [date]
 * address [varchar]
 * city [varchar]
 * state [varchar]
 * zipCode [varchar]
 * dateTimeStamp [date]


 * Matches table: has 2 foreign keys (userName1 and userName2 which connect to Person table)
 * userName1(fk) [varchar]   - is a person from the person table
 * userName2(fk) [varchar]  - is a person from the person table
 * matchCriteria [varchar]
 * matchPercent [int]
 * dateTimeStamp [date]


 * Searches table: has 1 foreign key (username which connects to the person starting the search in the Person table)
 * searchID (pk) [int]   - is auto incremented
 * userName(fk) [varchar] **** is the email of the person searching
 * firstName [varchar]
 * lastName [varchar]
 * status [varchar]
 * dateTimeStamp [date]


 * Emails table: has 1 foreign key (userName which connects to the Person table) it also has a composite primary key of (2) attributes: userName and email
 * userName (pk)(fk) [varchar]
 * email (pk) [varchar]
 * dateTimeStamp [date]


 * Phones table: has 1 foreign key (userName which connects to the Person table) it also has a composite primary key of (2) attributes: userName and phone
 * userName (pk)(fk) [varchar]
 * phone (pk) [varchar]
 * dateTimeStamp [date]

 The Revision Control -- Courtesy of Ryan O'Neal & Neil Champagne 

This week Neil and I successfully completed version control for the entire meandyou.us project. This included using TortoiseSVN and VirtualSVN to commit all the files into three seperate branches. They consist of the database, GUI, and match engine branches.

Screen shots of the SVN:





 The Wiki Specification -- Courtesy of Illya Kovarik 

You're reading it. Or read more here

Post 5/11/16

 * Thinking about database TTL (Time To Limit) or expiration.